登录成功后我们在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>
添加后的样子是:
附:cas-4.2.7搭建单点登录(3)---客户端demo编写-CSDN博客,我们前面讲的客户端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中加入客户端配置(配置方法见:cas-4.2.7搭建单点登录(3)---客户端demo编写-CSDN博客)
2.2 写一个接口(在controller中写一个方法),eg:
当我们浏览器访问:http://127.0.0.1:9000/test/aaa.do时,页面会跳转到cas登录界面
此时,我用自己的账号:dai/258369进行登录,登录成功后,页面会显示:
我们代码的控制台就会有输出:
值,我们拿到了,后续要进行怎么处理,那就看自己业务了。