cas-4.2.7搭建单点登录(6)---登录后返回mongo的用户表中的其他属性到客户端

登录成功后我们在cas client客户端获取用户名如下:

HttpServletRequest request = ServletActionContext.getRequest();
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
String username = principal.getName();

默认情况下cas server只返回用户名username到客户端,这可能不能够满足我们的业务,如用户登录后还需要返回用户的其他信息,如id、手机号、真实名字、年龄等,见如下步骤。

一、cas.properties配置文件新增属性

打开D:\casoverlay\cas-4.2.7\cas-server-webapp\src\main\webapp\WEB-INF路径下的cas.properties,增加红框内内容:

cas.authn.mongo.attributes=username,password,cellPhone,nickname,gender,age,roleName,deleted

红框内的等号后面的那些单词是用户表中的列的属性名称,需要那些属性就写那些即可。

二、自定义接口

1.IPersonAttributeDao (org.jasig.services.persondir.IPersonAttributeDao) 接口,这个是用来定义我们需要返回给客户端相关信息的接口

cas默认有提供许多实现,比如:

LdapPersonAttributeDao :通过查询 LDAP 目录 ,来返回信息

SingleRowJdbcPersonAttributeDao : 通过JDBC SQL查询,来返回信息

NamedStubPersonAttributeDao

StubPersonAttributeDao

等等,还有许多,大家可以参考源码中的实现,cas提供了各种功能的实现,有时候我们可以直接使用这个现成的就行了。 

2.mongodb在cas-4.2.x中是没有现成的接受属性转换接口的,所以我们自定义实现一个IPersonAttributeDao 。

2.1 在D:\casoverlay\cas-4.2.7\cas-server-support-mongo\src\main\java路径下新建文件夹,命名:self

2.2 在self文件夹内新建文件:MongoDBIPersonAttributes.java文件内容如下:

package self;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.jasig.services.persondir.IPersonAttributes;

public class MongoDBIPersonAttributes implements IPersonAttributes {

	/**
	* 
	*/
	private static final long serialVersionUID = 1634751256066729431L;

	private final Map<String, List<Object>> attributes;

	private String userNameAttribute;

	public MongoDBIPersonAttributes(String username, Map<String, List<Object>> attributes) {
		final Map<String, List<Object>> immutableValuesBuilder = this.buildImmutableAttributeMap(attributes);
		this.attributes = Collections.unmodifiableMap(immutableValuesBuilder);
		this.userNameAttribute = username;
	}

	@Override
	public String getName() {
		final Object attributeValue = this.getAttributeValue(this.userNameAttribute);
		if (attributeValue == null) {
			return null;
		}

		return attributeValue.toString();
	}

	@Override
	public Map<String, List<Object>> getAttributes() {
		return this.attributes;
	}

	@Override
	public Object getAttributeValue(String name) {
		final List<Object> values = this.attributes.get(name);
		if (values == null || values.size() == 0) {
			return null;
		}

		return values.get(0);
	}

	@Override
	public List<Object> getAttributeValues(String name) {
		final List<Object> values = this.attributes.get(name);
		if (values == null || values.size() == 0) {
			return null;
		}

		return values;
	}

	/**
	 * Take the constructor argument and convert the Map and List values into read-only form.
	 *
	 * @param attributes
	 *            Map of attributes
	 * @return Read-only map of attributes
	 */
	protected Map<String, List<Object>> buildImmutableAttributeMap(final Map<String, List<Object>> attributes) {
		final Map<String, List<Object>> immutableValuesBuilder = this.createImmutableAttributeMap(attributes.size());

		for (final Map.Entry<String, List<Object>> attrEntry : attributes.entrySet()) {
			final String key = attrEntry.getKey();
			List<Object> value = attrEntry.getValue();

			if (value != null) {
				value = Collections.unmodifiableList(value);
			}

			immutableValuesBuilder.put(key, value);
		}
		return immutableValuesBuilder;
	}

	/**
	 * Create the Map used to store the attributes internally for this IPersonAttributes
	 *
	 * @param size
	 *            size of map
	 * @return Map to store attributes
	 */
	protected Map<String, List<Object>> createImmutableAttributeMap(final int size) {
		return new LinkedHashMap<>(size > 0 ? size : 1);
	}

}

2.3 在self文件夹内新建文件:MongoDBPersonAttributeDao.java文件内容如下:

package self;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jasig.services.persondir.IPersonAttributeDao;
import org.jasig.services.persondir.IPersonAttributes;

public class MongoDBPersonAttributeDao implements IPersonAttributeDao {
	private IPersonAttributes backingPerson = null;
	Map<String, List<Object>> backingMap = new HashMap<String, List<Object>>();

	PersonMongoDB personMongoDB;

	@Override
	public IPersonAttributes getPerson(String uid) {
		if (uid == null) {
			throw new IllegalArgumentException("Illegal to invoke getPerson(String) with a null argument.");
		}
		this.backingPerson = this.personMongoDB.buildPersonMongoDB(uid, backingMap);
		return this.backingPerson;
	}

	@Override
	public Set<IPersonAttributes> getPeople(Map<String, Object> query) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<IPersonAttributes> getPeopleWithMultivaluedAttributes(Map<String, List<Object>> query) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<String> getPossibleUserAttributeNames() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<String> getAvailableQueryAttributes() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<String, List<Object>> getMultivaluedUserAttributes(Map<String, List<Object>> seed) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<String, List<Object>> getMultivaluedUserAttributes(String uid) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<String, Object> getUserAttributes(Map<String, Object> seed) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<String, Object> getUserAttributes(String uid) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * Set the Map which this stub object will return for all legal invocations of attributesForUser().
	 * 
	 * @param backingMap
	 *            The backingMap to set, may not be null.
	 */
	public void setBackingMap(final Map<String, List<Object>> backingMap) {
		this.backingMap = backingMap;
	}

	public void setPersonMongoDB(PersonMongoDB personMongoDB) {
		this.personMongoDB = personMongoDB;
	}

}

2.4 在self文件夹内新建文件:MongoDBIPersonAttributes.java文件内容如下:

package self;

import static com.mongodb.client.model.Filters.eq;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.bson.Document;
import org.jasig.services.persondir.IPersonAttributes;
import org.pac4j.core.exception.AccountNotFoundException;
import org.pac4j.core.exception.MultipleAccountsFoundException;
import org.pac4j.mongo.profile.MongoProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;

@Repository("personMongoDB")
public class PersonMongoDB {

	private static final Logger LOGGER = LoggerFactory.getLogger(PersonMongoDB.class);

	@Value("${cas.authn.mongo.collection.name:users}")
	private String collectionName;

	@Value("${cas.authn.mongo.db.name:cas}")
	private String databaseName;

	@Value("${cas.authn.mongo.db.host:}")
	private String mongoHostUri;

	@Value("${cas.authn.mongo.attributes:}")
	private String attributes;

	@Value("${cas.authn.mongo.username.attribute:username}")
	private String usernameAttribute;

	@Value("${cas.authn.mongo.password.attribute:password}")
	private String passwordAttribute;

	public PersonMongoDB() {
	}

	@SuppressWarnings("resource")
	public IPersonAttributes buildPersonMongoDB(String uid, Map<String, List<Object>> backingMap) {

		final String username = uid;
		final MongoClientURI uri = new MongoClientURI(this.mongoHostUri);
		final MongoClient mongoClient = new MongoClient(uri);
		LOGGER.info("Connected to MongoDb instance @ {} using database [{}]", uri.getHosts(), uri.getDatabase());
		final MongoDatabase db = mongoClient.getDatabase(uri.getDatabase());
		final MongoCollection<Document> collection = db.getCollection(collectionName);
		final List<Document> users = new ArrayList<>();

		try (final MongoCursor<Document> cursor = collection.find(eq(usernameAttribute, username)).iterator()) {

			int i = 0;
			while (cursor.hasNext() && i <= 2) {
				users.add(cursor.next());
				i++;
			}
		}

		if (!users.isEmpty()) {
			for (Document document : users) {
				if ((Boolean) document.get("deleted")) {
					users.remove(document);
				}
			}

			if (users.size() > 1) {
				throw new MultipleAccountsFoundException("Too many accounts found for: " + username);
			} else {
				Map<String, List<Object>> attributes = new HashMap<String, List<Object>>();
				final Map<String, Object> user = users.get(0);
				for (final Map.Entry<String, List<Object>> attrEntry : backingMap.entrySet()) {
					final String keydb = attrEntry.getKey();
					List<Object> value = attrEntry.getValue();
					if (value != null) {
						String keyClient = (String) value.get(0);
						List<Object> obs = new ArrayList<Object>();
						obs.add(user.get(keydb));
						attributes.put(keyClient, obs);
					}

				}
				IPersonAttributes person = new MongoDBIPersonAttributes(username, attributes);
				return person;
			}
		} else {
			throw new AccountNotFoundException("No account found for: " + username);
		}

	}

	protected MongoProfile createProfile(final String username, final String[] attributes, final Map<String, Object> result) {
		final MongoProfile profile = new MongoProfile();
		profile.setId(username);
		for (String attribute : attributes) {
			profile.addAttribute(attribute, result.get(attribute));
		}
		return profile;
	}

	public void setMongoHostUri(final String mongoHostUri) {
		this.mongoHostUri = mongoHostUri;
	}

	public void setCollectionName(final String collectionName) {
		this.collectionName = collectionName;
	}

	public void setDatabaseName(final String databaseName) {
		this.databaseName = databaseName;
	}

	public void setAttributes(final String attributes) {
		this.attributes = attributes;
	}

	public void setUsernameAttribute(final String usernameAttribute) {
		this.usernameAttribute = usernameAttribute;
	}

	public void setPasswordAttribute(final String passwordAttribute) {
		this.passwordAttribute = passwordAttribute;
	}

}

附:MongoDBIPersonAttributes.java中有一段代码:

由于用户是伪删除,用户删除后,用户表中该用户的“deleted”字段将变成true,初始状态(正常状态)的“false”,固此处要从List<Document> users中去除已经删除的用户。

三、修改cas-4.2.7中的配置

1.修改deployerConfigContext.xml

打开D:\casoverlay\cas-4.2.7\cas-server-webapp\src\main\webapp\WEB-INF下的deployerConfigContext.xml,找到

<util:map id="attrRepoBackingMap">
        <entry key="uid" value="uid" />
        <entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
        <entry key="groupMembership" value="groupMembership" />
        <entry>
            <key><value>memberOf</value></key>
            <list>
                <value>faculty</value>
                <value>staff</value>
                <value>org</value>
            </list>
        </entry>
    </util:map>

修改为:

    <!-- 要获取的属性在这里配置 -->
	<util:map id="attrRepoBackingMap">
		<!--value为提供给客户端获取的属性名字,key为对应的数据库字段名称,系统会自动填充值--> 
		<!-- username,password,cellPhone,nickname,gender,age,roleName,deleted -->
		<entry value="username" key="username"/>
		<entry value="password" key="password"/>
		<entry value="cellPhone" key="cellPhone"/>
		<entry value="nickname" key="nickname"/>
		<entry value="gender" key="gender"/>
		<entry value="age" key="age"/>
		<entry value="roleName" key="roleName"/>
		<entry value="deleted" key="deleted"/>
	</util:map>

PersonMongoDB.java类需要数据相关属性,在使用过程中需要把self.PersonMongoDB当作bean传进去,这样才能personMongoDB才能获取到@value中的mongodb配置参数值。

因此在此配置中还需加入一句:

<bean id="personMongoDB" class="self.PersonMongoDB" />

完成后效果如下:

2.修改cas-4.2.7中的casServiceValidationSuccess.jsp文件

此文件是将用户登录成功后,将信息生成XML传递给客户端,原文件是只包含name信息,所以需要修改,新增获取其他属性的代码。

D:\casoverlay\cas-4.2.7\cas-server-webapp\src\main\webapp\WEB-INF\view\jsp\protocol\2.0下有casServiceValidationSuccess.jsp文件,

D:\casoverlay\cas-4.2.7\cas-server-webapp\src\main\webapp\WEB-INF\view\jsp\protocol\3.0下也有casServiceValidationSuccess.jsp文件

不知道修改哪个时,两个都一起修改了,后面再讲。

找到红框的位置:

紧跟着红框下面添加代码:

<c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">   
            <cas:attributes>   
                <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">                             
                    <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>                                 
                </c:forEach>     
            </cas:attributes>   
        </c:if> 

添加后的样子是:

附:https://blog.csdn.net/Dai_Haijiao/article/details/87970718,我们前面讲的客户端demo中的web.xml中有配置:

若红框内属性值为:org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter时,D:\casoverlay\cas-4.2.7\cas-server-webapp\src\main\webapp\WEB-INF\view\jsp\protocol\3.0下的casServiceValidationSuccess.jsp文件是生效的

若红框内属性值为:org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter时,D:\casoverlay\cas-4.2.7\cas-server-webapp\src\main\webapp\WEB-INF\view\jsp\protocol\2.0下的casServiceValidationSuccess.jsp文件是生效的

而客户端的代码并非我们写,可能来自不同人的代码,有的用的是2.0,有点用的是3.0,所以...,自己看着办。

四、测试

1.按照之前的步骤,对cas服务端进行打包,部署,运行。

2.客户端代码修改

2.1 随便找个web项目,在web.xml中加入客户端配置(配置方法见:https://blog.csdn.net/Dai_Haijiao/article/details/87970718

2.2 写一个接口(在controller中写一个方法),eg:

当我们浏览器访问:http://127.0.0.1:9000/test/aaa.do时,页面会跳转到cas登录界面

此时,我用自己的账号:dai/258369进行登录,登录成功后,页面会显示:

我们代码的控制台就会有输出:

值,我们拿到了,后续要进行怎么处理,那就看自己业务了。

猜你喜欢

转载自blog.csdn.net/Dai_Haijiao/article/details/88018002