Getting more attributes from CAS than just user id
I am using CAS with JDBC Authentication handler and was wondering is it possible to get the other attributes of principal object (for e.g. firstname, lastname) not just the username from CAS after successful 开发者_如何学Cauthentication?
In the casServiceValidationSuccess.jsp, I add like below:
<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>
In the deployerConfigContent.xml, I add like below:
<bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" >
**<property name="attributeRepository">
<ref bean="attributeRepository" />
</property>**
</bean>
<bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">
<constructor-arg index="0" ref="dataSource"/>
<constructor-arg index="1" value="select * from bbs_members where {0}" />
<property name="queryAttributeMapping">
<map>
<entry key="username" value="username" />
</map>
</property>
<property name="resultAttributeMapping">
<map>
<entry key="uid" value="uid"/>
<entry key="email" value="email"/>
<entry key="password" value="password"/>
</map>
</property>
</bean>
It works.
I came across this problem during the debug, please close the browser if you change this JSP or XML files, otherwise the changes won't work. Be careful.
To get any user attributes from DB I did the following: use PersonDirectoryPrincipalResolver
in deployerConfigContext.xml:
<bean id="primaryPrincipalResolver"
class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" >
<property name="attributeRepository" ref="singleRowJdbcPersonMultiplyAttributeDao" />
</bean>
instead of using standard SingleRowJdbcPersonAttributeDao class create your own implementation which returns not only one row from a query result but aggregated data from all returned rows:
copy all code from SingleRowJdbcPersonAttributeDao
and change only one method parseAttributeMapFromResults
.
you will have something like that:
public class SingleRowJdbcPersonMultiplyAttributeDao extends AbstractJdbcPersonAttributeDao<Map<String, Object>> {
...
@Override
protected List<IPersonAttributes> parseAttributeMapFromResults(final List<Map<String, Object>> queryResults, final String queryUserName) {
final List<IPersonAttributes> peopleAttributes = new ArrayList<IPersonAttributes>(queryResults.size());
Map<String, List<Object>> attributes = new HashMap<String, List<Object>>();
for (final Map<String, Object> queryResult : queryResults) {
for (final Map.Entry<String, Object> seedEntry : queryResult.entrySet()) {
final String seedName = seedEntry.getKey();
final Object seedValue = seedEntry.getValue();
if (attributes.get(seedName) != null && !attributes.get(seedName).get(0).equals(seedValue)) {
attributes.get(seedName).add(seedValue);
} else {
List<Object> list = new ArrayList<Object>();
list.add(seedValue);
attributes.put(seedName, list);
}
}
}
final IPersonAttributes person;
final String userNameAttribute = this.getConfiguredUserNameAttribute();
if (this.isUserNameAttributeConfigured() && attributes.containsKey(userNameAttribute)) {
// Option #1: An attribute is named explicitly in the config,
// and that attribute is present in the results from LDAP; use it
person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, attributes);
} else if (queryUserName != null) {
// Option #2: Use the userName attribute provided in the query
// parameters. (NB: I'm not entirely sure this choice is
// preferable to Option #3. Keeping it because it most closely
// matches the legacy behavior there the new option -- Option #1
// -- doesn't apply. ~drewwills)
person = new CaseInsensitiveNamedPersonImpl(queryUserName, attributes);
} else {
// Option #3: Create the IPersonAttributes doing a best-guess
// at a userName attribute
person = new CaseInsensitiveAttributeNamedPersonImpl(userNameAttribute, attributes);
}
peopleAttributes.add(person);
return peopleAttributes;
}
...
}
and in deployerConfigContext.xml:
<bean id="singleRowJdbcPersonMultiplyAttributeDao"
class="com.scentbird.SingleRowJdbcPersonMultiplyAttributeDao">
<constructor-arg index="0" ref="dataSource" />
<constructor-arg index="1" value="SELECT attributes_table1.*, attributes_table2.attr1, attributes_table2.roles AS roles FROM user_table ut LEFT JOIN roles_table rt ON <condition> LEFT JOIN another_table at ON <condition> WHERE {0}" />
<property name="queryAttributeMapping">
<map>
<entry key="username" value="username" />
</map>
</property>
</bean>
Also in my case I used SAML protocol.
As a result you will get on the client all attributes which your select returns. For example, if user have many roles you could have on the client:
User: username, firstname, lastname, email, ... , [ROLE_1, ROLE_2, ROLE_3]
My case works with Spring Security and Grails.
I'm not sure this is 100% Feng Shui solution :) as it's fast cooked but it works in our case.
Hope it helps.
I just spent the last three days attempting to get CAS properly configured. One of the issues I encountered was that I had to explicitly instruct CAS to publish the properties. I did this by:
- opening https://localhost/cas/services
- going to the 'Manage Services' tab
- click 'edit' for each service
- highlight the properties you wish to publish
- click the save button
FWIW, the other issue is that casServiceValidationSuccess.jsp does contain any code to pass the properties back in the response. I was looking for a solution to this when I found your question. I notice that you have rewritten your implementation.
The definitive and complete solution is the following (for this undocumented feature):
Server side:
a. Add an
attributeRepository
to yourCredentialsToPrincipalResolver
.b. Implement the
your.package.YourPersonAttributeDao
like anIPersonAttributeDao
.c. Declare the attributes that will be transmitted into assertion to client.
d. Modify the
casServiceValidationSuccess.jsp
to display the attributes (thx to xiongjiabin).Client side. You get all attributes by doing this:
Due to formatting problem I can't post the code of the definitive solution.... Let me know if you are interested, I will send you an email with all the code.
In addition to the answer provided by @xiongjiabin if you are using CAS v4+ you probably want to use assertion.primaryAuthentication
instead of assertion.chainedAuthentications
in casServiceValidationSuccess.jsp
:
<cas:attributes>
<c:forEach var="attr" items="${assertion.primaryAuthentication.principal.attributes}">
<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>**
</c:forEach>
</cas:attributes>
If you do use assertion.chainedAuthentications
with CAS v4+ then the serviceRegistryDao
list of allowedAttributes
will be ignored and all attributes will be returned.
精彩评论