CAS3.5.2 Server登录后返回用户信息详细解决方案

361 阅读5分钟

转载自:blog.yoodb.com/yoodb/artic…

        单点登录(Single Sign-On, 简称SSO)是目前比较流行的服务于企业业务整合的解决方案之一,SSO使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。大家在使用时CAS Server验证成功后会立即跳转到客户端,CAS Server默认只返回账号uid给客户端,如何定义CAS server返回信息到CAS Client客户端(CAS server返回更多用户信息)呢?下面本站素文宅www.yoodb.com本篇文章具体讲解一下怎么实现,仅供大家参考学习。

\

1、相关接口

首先了解一下相关的几个接口

•  Credentials

•  Principal

•  IPersonAttributeDao

•  CredentialsToPrincipalResolver

1)org.jasig.cas.authentication.principal.Credentials接口, 在工程中UsernamePasswordCredential 的类就是实现了Credentials接口。这个接口是用来定义我们在登录时输入的认证信息,比如用户名、密码、验证码等,可以理解为用户认证的相关凭据。

2)org.jasig.cas.authentication.principal.Principal接口,主要是用来保存用户认证后的用户信息,信息保存在一个Map集合中。

3)org.jasig.services.persondir.IPersonAttributeDao接口,是用来定义我们需要返回给客户端相关信息的接口,CAS Server默认有提供许多实现,比如

•  SingleRowJdbcPersonAttributeDao : JDBC SQL查询返回信息

•  LdapPersonAttributeDao :查询 LDAP 目录返回信息

•  StubPersonAttributeDao :自定义属性返回信息

等参考源码中的实现,CAS Server提供了各种功能的实现

4)org.jasig.cas.authentication.principal. CredentialsToPrincipalResolver接口,主要是用来把Credentials里面的信息转换到 Principal中,在这接口中有两个方法,具体如下:\

Principal resolvePrincipal(Credentials credentials);// 解析Credentials中的信息,返回 Principal 接口

boolean supports(Credentials credentials); //判断Credentials 是否支持 Principal 协议

注意:在3.x版本是CredentialsToPrincipalResolver接口,4.x版本是PrincipalResolver接口

\

2、CAS Server流程

打开 deployerConfigContext.xml 文件,CAS Server3.5.2版本默认配置了org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver类,它注入一个 attributeRepository 属性,实现IPersonAttributeDao 接口调用getPerson方法,传入principalId参数,也就是调用Credentials 接口的getId() 方法,配置文件详情具体如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238<?xml version= "1.0"  encoding= "UTF-8" ?>``<!--``     Licensed to Jasig under one or  more  contributor license``     agreements. See the NOTICE  file  distributed with this work``     for  additional information regarding copyright ownership.``     Jasig licenses this  file  to you under the Apache License,``     Version 2.0 (the  "License" ); you may not use this  file``     except  in  compliance with the License.  You may obtain a``     copy of the License at the following location:``       http: //www .apache.org /licenses/LICENSE-2 .0``     Unless required by applicable law or agreed to  in  writing,``     software distributed under the License is distributed on an``     "AS IS"  BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY``     KIND, either express or implied.  See the License  for  the``     specific language governing permissions and limitations``     under the License.``-->``<!--``| deployerConfigContext.xml centralizes into one  file  some of the declarative configuration that``| all CAS deployers will need to modify.``|``| This  file  declares some of the Spring-managed JavaBeans that  make  up a CAS deployment. ``| The beans declared  in  this  file  are instantiated at context initialization  time  by the Spring``| ContextLoaderListener declared  in  web.xml.  It finds this  file  because this``|  file  is among those declared  in  the context parameter  "contextConfigLocation" .``|``| By far the most common change you will need to  make  in  this  file  is to change the last bean``| declaration to replace the default SimpleTestUsernamePasswordAuthenticationHandler with``| one implementing your approach  for  authenticating usernames and passwords.``+-->``<beans xmlns= "http://www.springframework.org/schema/beans"``        xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"``        xmlns:p= "http://www.springframework.org/schema/p"``        xmlns:tx= "http://www.springframework.org/schema/tx"``        xmlns:sec= "http://www.springframework.org/schema/security"``        xsi:schemaLocation="http: //www .springframework.org /schema/beans  http: //www .springframework.org /schema/beans/spring-beans-3 .1.xsd``        http: //www .springframework.org /schema/tx  http: //www .springframework.org /schema/tx/spring-tx-3 .1.xsd``        http: //www .springframework.org /schema/security  http: //www .springframework.org /schema/security/spring-security-3 .1.xsd">``<!-- ldap 数据源 -->``<bean  id = "contextSource"  class= "org.springframework.ldap.core.support.LdapContextSource" >``<property name= "pooled"  value= "false"  />``<property name= "urls" >``<list>``  <value>ldap: //127 .0.0.1:1389< /value >``< /list >``< /property >``<property name= "userDn"  value= "cn=Directory Manager"  />``<property name= "password"  value= "123456"  />``<property name= "baseEnvironmentProperties" >``<map>``<entry>``<key>``<value>java.naming.security.authentication< /value >``< /key >``<value>simple< /value >``<!--<value>none< /value > -->``< /entry >``< /map >``< /property >``< /bean >``<!--``| This bean declares our AuthenticationManager.  The CentralAuthenticationService service bean``| declared  in  applicationContext.xml picks up this AuthenticationManager by reference to its  id ,``|  "authenticationManager" .  Most deployers will be able to use the default AuthenticationManager``| implementation and so  do  not need to change the class of this bean.  We include the whole``| AuthenticationManager here  in  the userConfigContext.xml so that you can see the things you will``| need to change  in  context.``+-->``<bean  id = "authenticationManager"``class= "org.jasig.cas.authentication.AuthenticationManagerImpl" >``<!-- Uncomment the metadata populator to allow clearpass to capture and cache the password``      This switch effectively will turn on clearpass.``<property name= "authenticationMetaDataPopulators" >``    <list>``       <bean class= "org.jasig.cas.extension.clearpass.CacheCredentialsMetaDataPopulator" >``          <constructor-arg index= "0"  ref= "credentialsCache"  />``       < /bean >``    < /list >``< /property >``  -->``<!--``| This is the List of CredentialToPrincipalResolvers that identify what Principal is trying to authenticate.``| The AuthenticationManagerImpl considers them  in  order, finding a CredentialToPrincipalResolver  which``| supports the presented credentials.``|``| AuthenticationManagerImpl uses these resolvers  for  two purposes.  First, it uses them to identify the Principal``| attempting to authenticate to CAS  /login  .  In the default configuration, it is the DefaultCredentialsToPrincipalResolver``| that fills this role.  If you are using some other kind of credentials than UsernamePasswordCredentials, you will need to replace``| DefaultCredentialsToPrincipalResolver with a CredentialsToPrincipalResolver that supports the credentials you are``| using.``|``| Second, AuthenticationManagerImpl uses these resolvers to identify a service requesting a proxy granting ticket.``| In the default configuration, it is the HttpBasedServiceCredentialsToPrincipalResolver that serves this purpose.``| You will need to change this list  if  you are identifying services by something  more  or other than their callback URL.``+-->``<property name= "credentialsToPrincipalResolvers" >``<list>``<!--``| UsernamePasswordCredentialsToPrincipalResolver supports the UsernamePasswordCredentials that we use  for  /login``| by default and produces SimplePrincipal instances conveying the username from the credentials.``|``| If you've changed your LoginFormAction to use credentials other than UsernamePasswordCredentials  then  you will also``| need to change this bean declaration (or add additional declarations) to  declare  a CredentialsToPrincipalResolver that supports the``| Credentials you are using.``+-->``<bean class= "org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver"  >``<property name= "attributeRepository"  ref= "attributeRepository"  />``< /bean >``<!--``| HttpBasedServiceCredentialsToPrincipalResolver supports HttpBasedCredentials.  It supports the CAS 2.0 approach of``| authenticating services by SSL callback, extracting the callback URL from the Credentials and representing it as a``| SimpleService identified by that callback URL.``|``| If you are representing services by something  more  or other than an HTTPS URL whereat they are able to``| receive a proxy callback, you will need to change this bean declaration (or add additional declarations).``+-->``<bean``class= "org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver"  />``< /list >``< /property >``<!--``| Whereas CredentialsToPrincipalResolvers identify  who  it is some Credentials might authenticate,``| AuthenticationHandlers actually authenticate credentials.  Here we  declare  the AuthenticationHandlers that``| authenticate the Principals that the CredentialsToPrincipalResolvers identified.  CAS will try these handlers  in  turn``|  until  it finds one that both supports the Credentials presented and succeeds  in  authenticating.``+-->``<property name= "authenticationHandlers" >``<list>``<!--``| This is the authentication handler that authenticates services by means of callback via SSL, thereby validating``| a server side SSL certificate.``+-->``<bean class= "org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"``p:httpClient-ref= "httpClient"  />``<!--``| This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS``| into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials``| where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your``|  local  authentication strategy.  You might accomplish this by coding a new such handler and declaring``| edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided  in  the adaptors modules.``+-->``<!-- <bean``class= "org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler"  /> -->``<!-- ldap认证 -->``    <bean class= "org.jasig.cas.adaptors.ldap.BindLdapAuthenticationHandler" >``<property name= "filter"  value= "uid=%u"  />``<property name= "searchBase"  value= "ou=users,dc=CMP" />``<property name= "contextSource"  ref= "contextSource"  />``<!-- <property name= "requireAllQueryAttributes"  value= "true"  /> -->``< /bean >  ``< /list >``< /property >``< /bean >``<!--``This bean defines the security roles  for  the Services Management application.  Simple deployments can use the  in -memory version.``More robust deployments will want to use another option, such as the Jdbc version.``The name of this should remain  "userDetailsService"  in  order  for  Spring Security to  find  it.``  -->``     <!-- <sec:user name= "@@THIS SHOULD BE REPLACED@@"  password= "notused"  authorities= "ROLE_ADMIN"  />-->``     <sec:user-service  id = "userDetailsService" >``         <sec:user name= "@@THIS SHOULD BE REPLACED@@"  password= "notused"  authorities= "ROLE_ADMIN"  />``     < /sec :user-service>``<!--``Bean that defines the attributes that a service may  return .  This example uses the Stub /Mock  version.  A real implementation``may go against a database or LDAP server.  The  id  should remain  "attributeRepository"  though.``<bean  id = "attributeRepository"``class= "org.jasig.services.persondir.support.StubPersonAttributeDao" >``  -->``  <bean  id = "attributeRepository"``class= "org.jasig.cas.util.ShiroStubPersonAttributeDao" >``<property name= "backingMap" >``<map>``<entry key= "uid"  value= "uid"  />``<entry key= "username"  value= "username" />``<entry key= "password"  value= "password" />``<entry key= "eduPersonAffiliation"  value= "eduPersonAffiliation"  />``<entry key= "groupMembership"  value= "groupMembership"  />``< /map >``< /property >``< /bean >``<!--``Sample,  in -memory data store  for  the ServiceRegistry. A real implementation``would probably want to replace this with the JPA-backed ServiceRegistry DAO``The name of this bean should remain  "serviceRegistryDao" .``  -->``<bean``id = "serviceRegistryDao"``         class= "org.jasig.cas.services.InMemoryServiceRegistryDaoImpl" >``             <property name= "registeredServices" >``                 <list>``                     <bean class= "org.jasig.cas.services.RegexRegisteredService" >``                         <property name= "id"  value= "0"  />``                         <property name= "name"  value= "HTTP and IMAP"  />``                         <property name= "description"  value= "Allows HTTP(S) and IMAP(S) protocols"  />``                         <property name= "serviceId"  value= "^(https?|imaps?)://.*"  />``                         <property name= "evaluationOrder"  value= "10000001"  />``                         <property name= "ignoreAttributes"  value= "true" />``                     < /bean >``                     <!--``                     Use the following definition instead of the above to further restrict access``                     to services within your domain (including subdomains).``                     Note that example.com must be replaced with the domain you wish to permit.``                     -->``                     <!--``                     <bean class= "org.jasig.cas.services.RegexRegisteredService" >``                         <property name= "id"  value= "1"  />``                         <property name= "name"  value= "HTTP and IMAP on example.com"  />``                         <property name= "description"  value= "Allows HTTP(S) and IMAP(S) protocols on example.com"  />``                         <property name= "serviceId"  value= "^(https?|imaps?)://([A-Za-z0-9_-]+\.)*example\.com/.*"  />``                         <property name= "evaluationOrder"  value= "0"  />``                     < /bean >``                     -->``                 < /list >``             < /property >``         < /bean >``   <bean  id = "auditTrailManager"  class= "com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager"  />``      <bean  id = "healthCheckMonitor"  class= "org.jasig.cas.monitor.HealthCheckMonitor" >``     <property name= "monitors" >``       <list>``         <bean class= "org.jasig.cas.monitor.MemoryMonitor"``             p:freeMemoryWarnThreshold= "10"  />``         <!--``           NOTE``           The following ticket registries support SessionMonitor:``             * DefaultTicketRegistry``             * JpaTicketRegistry``           Remove this monitor  if  you use an unsupported registry.``         -->``         <bean class= "org.jasig.cas.monitor.SessionMonitor"``             p:ticketRegistry-ref= "ticketRegistry"``             p:serviceTicketCountWarnThreshold= "5000"``             p:sessionCountWarnThreshold= "100000"  />``       < /list >``     < /property >``   < /bean >``< /beans >

新增以及修改内容,具体如下:

123456789101112<bean  id = "attributeRepository"``class= "org.jasig.cas.util.ShiroStubPersonAttributeDao" >``<property name= "backingMap" >``<map>``<entry key= "uid"  value= "uid"  />``<entry key= "username"  value= "username" />``<entry key= "password"  value= "password" />``<entry key= "eduPersonAffiliation"  value= "eduPersonAffiliation"  />``<entry key= "groupMembership"  value= "groupMembership"  />``< /map >``< /property >``< /bean >

调试源码会发现org.jasig.cas.CentralAuthenticationServiceImpl.java类中有判断是否允许客户端获取数据信息,代码如下:

12345678if  (!registeredService.isIgnoreAttributes()) {``                 final Map<String, Object> attributes = new HashMap<String, Object>();``                 for  (final String attribute : registeredService.getAllowedAttributes()) {``                     final Object value = principal.getAttributes().get(attribute);``                     if  (value != null) {``                         attributes.put(attribute, value);``                     }``                 }

下面配置将ignoreAttributes属性值设置为true,允许客户端获取数据,具体配置如下:

1<property name= "ignoreAttributes"  value= "true" />

\

重写org.jasig.services.persondir.support.StubPersonAttributeDao类中getPerson方法,实现多用户信息具体代码如下:

12345678910111213141516171819package  org.jasig.cas.util;``import  java.util.Collections;``import  java.util.HashMap;``import  java.util.List;``import  java.util.Map;``import  org.jasig.services.persondir.IPersonAttributes;``import  org.jasig.services.persondir.support.AttributeNamedPersonImpl;``import  org.jasig.services.persondir.support.StubPersonAttributeDao;``public  class  ShiroStubPersonAttributeDao   extends  StubPersonAttributeDao{``@Override``     public  IPersonAttributes getPerson(String uid) {``                  Map<String, List<Object>> attributes =  new  HashMap<String, List<Object>>();``         attributes.put( "uid" , Collections.singletonList((Object)uid));``         attributes.put( "username" , Collections.singletonList((Object) "www.yoodb.com" ));``         attributes.put( "password" , Collections.singletonList((Object) "123456" ));``         return  new  AttributeNamedPersonImpl(attributes);``     }``}

\

修改cas-server-webapp工程中路径/cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp文件内容,具体如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445<%--``     Licensed to Jasig under one or more contributor license``     agreements. See the NOTICE file distributed with  this  work``     for  additional information regarding copyright ownership.``     Jasig licenses  this  file to you under the Apache License,``     Version  2.0  (the  "License" ); you may not use  this  file``     except in compliance with the License.  You may obtain a``     copy of the License at the following location:``       http: //www.apache.org/licenses/LICENSE-2.0``     Unless required by applicable law or agreed to in writing,``     software distributed under the License is distributed on an``     "AS IS"  BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY``     KIND, either express or implied.  See the License  for  the``     specific language governing permissions and limitations``     under the License.``  --%>``<%@ page session= "false"  %>``<%@ taglib prefix= "c"  uri= "http://java.sun.com/jsp/jstl/core"  %>``<%@ taglib uri= "http://java.sun.com/jsp/jstl/functions"  prefix= "fn"  %>``<cas:serviceResponse xmlns:cas= 'http://www.yale.edu/tp/cas' >``<cas:authenticationSuccess>``<cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)- 1 ].principal.id)}</cas:user>``<!--pros Begin-->``<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 >``<!--pros End-->``<c: if  test= "${not empty pgtIou}" >``<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>``</c: if >``<c: if  test= "${fn:length(assertion.chainedAuthentications) > 1}" >``<cas:proxies>``<c:forEach var= "proxy"  items= "${assertion.chainedAuthentications}"  varStatus= "loopStatus"  begin= "0"  end= "${fn:length(assertion.chainedAuthentications)-2}"``step= "1" >``<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>``</c:forEach>``</cas:proxies>``</c: if >``</cas:authenticationSuccess>``</cas:serviceResponse>

新增内容,具体如下:

1234567<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 >

\

3、CAS Client流程

在CAS Client设置信息接收,java可以通过下面的方式获取,代码如下:

123AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();``Map attributes = principal.getAttributes();``String uid = attributes .get( "uid" );

\

CAS Client如果集成了shiro权限控制,由于cas client将casFilter交给了shiroFilter来控制,接受数据信息代码如下:

12345Subject subject = SecurityUtils.getSubject();``List list = subject.getPrincipals().asList();``String name = (String) list.get( 0 ); //账号``Map<String, Object> info = (Map<String, Object>)list.get( 1 ); //Json串``String uid = (String) info.get( "uid" );