CAS解決單點登錄SSO

關於CAS很多的原理和基礎的配置啓動,網上是很多的,我更多是結合我的實踐和心得。需要了解CAS的原理,認證協議,認證流程,可以參考以下文章

讓CAS支持客戶端自定義登陸頁面——客戶端篇

CAS原理與配置-基於CAS的單點登陸的研究(上)

服務端配置

CAS單點登陸部署

CAS配置手冊

CAS單點登錄配置


背景

單點登錄(SSO)是企業開發的重要問題,在我的畢設項目中,由於要和系統其他開發模塊共用用戶認證模塊,方便共享用戶資源,所以需要一個好的SSO解決方案。

一般SSO的實現機制有兩種:基於session的和基於cookie的。WebLogic通過Session共享認證信息。Session是一種服務器端機制,當客戶端訪問服務器時,服務器爲客戶端創建一個惟一的SessionID,以使在整個交互過程中始終保持狀態,而交互的信息則可由應用自行指定,因此用Session方式實現SSO,不能在多個瀏覽器之間實現單點登錄,但卻可以跨域;WebSphere通過Cookie記錄認證信息。Cookie是一種客戶端機制,它存儲的內容主要包括: 名字、值、過期時間、路徑和域,路徑與域合在一起就構成了Cookie的作用範圍,因此用Cookie方式可實現SSO,但域名必須相同。對應這兩者,開源的SSO實現分別是OAuth和CAS。

OAuth更多的是解決第三方去訪問服務提供方的用戶的資源,我認爲更適用於不同的系統,比如大的平臺都會提供OAuth的認證機制(新浪微博,google)。而CAS更貼近我的需求,就是解決同一系統下不同服務間的用戶認證工作,可以無縫連接。


關於CAS

CAS是Yale大學開源項目,是企業級單點登錄解決方案。是部署在Tomcat上的獨立的web應用。CAS-Server是單獨部署的認證服務器,客戶端即各服務開發者需要使用CAS-Client包。爲了認證的安全性,CAS-server需要Tomcat開啓SSL的https端口,並生成,導入和導出證書(一般可以用jre的keytool生成)。其實也可以取消cas的https。CAS是一個基於Spring框架開發的東西,下載來的war包放到tomcat的webapp裏就可以用。往往這樣的開源工具的特點就是,配置比較複雜,或者說基礎配置是簡單的,進階配置是很麻煩的,你可能要去看源碼,要去研究很多細小的東西,這個和solr一樣。而顯然他的優點就是定製和擴展。

我的CAS配置和實踐
我使用的是cas-server-3.4.10和cas-client-3.2.1。先說cas-server-3.4.10的一些配置。deployerConfigContext.xml裏,一般我們需要添加的是:使用我們自己的身份驗證方式;獲取更多用戶信息放到session裏讓客戶端獲得。我對deployerConfigContext.xml的修改是使用jdbc連接mysql認證,並且返回除了username外其他的email等信息,

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!--  
  3.     | deployerConfigContext.xml centralizes into one file some of the declarative configuration that  
  4.     | all CAS deployers will need to modify.  
  5.     |  
  6.     | This file declares some of the Spring-managed JavaBeans that make up a CAS deployment.    
  7.     | The beans declared in this file are instantiated at context initialization time by the Spring   
  8.     | ContextLoaderListener declared in web.xml.  It finds this file because this  
  9.     | file is among those declared in the context parameter "contextConfigLocation".  
  10.     |  
  11.     | By far the most common change you will need to make in this file is to change the last bean  
  12.     | declaration to replace the default SimpleTestUsernamePasswordAuthenticationHandler with  
  13.     | one implementing your approach for authenticating usernames and passwords.  
  14.     +-->  
  15. <beans xmlns="http://www.springframework.org/schema/beans"  
  16.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  17.        xmlns:p="http://www.springframework.org/schema/p"  
  18.        xmlns:sec="http://www.springframework.org/schema/security"  
  19.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  20.        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">  
  21.     <!--  
  22.         | This bean declares our AuthenticationManager.  The CentralAuthenticationService service bean  
  23.         | declared in applicationContext.xml picks up this AuthenticationManager by reference to its id,   
  24.         | "authenticationManager".  Most deployers will be able to use the default AuthenticationManager  
  25.         | implementation and so do not need to change the class of this bean.  We include the whole  
  26.         | AuthenticationManager here in the userConfigContext.xml so that you can see the things you will  
  27.         | need to change in context.  
  28.         +-->  
  29.     <bean id="authenticationManager"  
  30.         class="org.jasig.cas.authentication.AuthenticationManagerImpl">  
  31.         <!--  
  32.             | This is the List of CredentialToPrincipalResolvers that identify what Principal is trying to authenticate.  
  33.             | The AuthenticationManagerImpl considers them in order, finding a CredentialToPrincipalResolver which   
  34.             | supports the presented credentials.  
  35.             |  
  36.             | AuthenticationManagerImpl uses these resolvers for two purposes.  First, it uses them to identify the Principal  
  37.             | attempting to authenticate to CAS /login .  In the default configuration, it is the DefaultCredentialsToPrincipalResolver  
  38.             | that fills this role.  If you are using some other kind of credentials than UsernamePasswordCredentials, you will need to replace  
  39.             | DefaultCredentialsToPrincipalResolver with a CredentialsToPrincipalResolver that supports the credentials you are  
  40.             | using.  
  41.             |  
  42.             | Second, AuthenticationManagerImpl uses these resolvers to identify a service requesting a proxy granting ticket.   
  43.             | In the default configuration, it is the HttpBasedServiceCredentialsToPrincipalResolver that serves this purpose.   
  44.             | You will need to change this list if you are identifying services by something more or other than their callback URL.  
  45.             +-->  
  46.         <property name="credentialsToPrincipalResolvers">  
  47.             <list>  
  48.                 <!--  
  49.                     | UsernamePasswordCredentialsToPrincipalResolver supports the UsernamePasswordCredentials that we use for /login   
  50.                     | by default and produces SimplePrincipal instances conveying the username from the credentials.  
  51.                     |   
  52.                     | If you've changed your LoginFormAction to use credentials other than UsernamePasswordCredentials then you will also  
  53.                     | need to change this bean declaration (or add additional declarations) to declare a CredentialsToPrincipalResolver that supports the  
  54.                     | Credentials you are using.  
  55.                     +-->  
  56.                 <bean  
  57.                     class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" >  
  58.                     <property name="attributeRepository" ref="attributeRepository" />  
  59.                 </bean>  
  60.                 <!--  
  61.                     | HttpBasedServiceCredentialsToPrincipalResolver supports HttpBasedCredentials.  It supports the CAS 2.0 approach of  
  62.                     | authenticating services by SSL callback, extracting the callback URL from the Credentials and representing it as a  
  63.                     | SimpleService identified by that callback URL.  
  64.                     |  
  65.                     | If you are representing services by something more or other than an HTTPS URL whereat they are able to  
  66.                     | receive a proxy callback, you will need to change this bean declaration (or add additional declarations).  
  67.                     +-->  
  68.                 <bean  
  69.                     class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />  
  70.             </list>  
  71.         </property>  
  72.   
  73.         <!--  
  74.             | Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate,   
  75.             | AuthenticationHandlers actually authenticate credentials.  Here we declare the AuthenticationHandlers that  
  76.             | authenticate the Principals that the CredentialsToPrincipalResolvers identified.  CAS will try these handlers in turn  
  77.             | until it finds one that both supports the Credentials presented and succeeds in authenticating.  
  78.             +-->  
  79.         <property name="authenticationHandlers">  
  80.             <list>  
  81.                 <!--  
  82.                     | This is the authentication handler that authenticates services by means of callback via SSL, thereby validating  
  83.                     | a server side SSL certificate.  
  84.                     +-->  
  85.                 <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"  
  86.                     p:httpClient-ref="httpClient" p:requireSecure="false" />  
  87.                 <!--  
  88.                     | This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS   
  89.                     | into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials  
  90.                     | where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your  
  91.                     | local authentication strategy.  You might accomplish this by coding a new such handler and declaring  
  92.                     | edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.  
  93.                     +-->  
  94.                 <!--  
  95.                 <bean  
  96.                     class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />  
  97.                 -->  
  98.                   
  99.                 <bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">  
  100.                     <property name="dataSource" ref="dataSource"></property>  
  101.                     <property name="sql" value="select password from academic_user where username=?"></property>  
  102.                     <!-- 
  103.                     <property name="passwordEncoder" ref="MD5PasswordEncoder"></property> 
  104.                     -->  
  105.                 </bean>  
  106.               
  107.             </list>  
  108.         </property>  
  109.     </bean>  
  110.   
  111.     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
  112.         <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>  
  113.         <property name="url"><value>jdbc:mysql://localhost:3307/academic</value></property>  
  114.         <property name="username"><value>root</value></property>  
  115.         <property name="password"><value></value></property>  
  116.     </bean>  
  117.  <!--  
  118.     <bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">  
  119.         <constructor-arg index="0">  
  120.             <value>MD5</value>  
  121.         </constructor-arg>  
  122.     </bean>  
  123. -->  
  124.   
  125.   
  126.     <!--  
  127.     This bean defines the security roles for the Services Management application.  Simple deployments can use the in-memory version.  
  128.     More robust deployments will want to use another option, such as the Jdbc version.  
  129.       
  130.     The name of this should remain "userDetailsService" in order for Spring Security to find it.  
  131.      -->  
  132.     <!-- <sec:user name="@@THIS SHOULD BE REPLACED@@" password="notused" authorities="ROLE_ADMIN" />-->  
  133.   
  134.     <sec:user-service id="userDetailsService">  
  135.         <sec:user name="@@THIS SHOULD BE REPLACED@@" password="notused" authorities="ROLE_ADMIN" />  
  136.     </sec:user-service>  
  137.       
  138.     <!--   
  139.     Bean that defines the attributes that a service may return.  This example uses the Stub/Mock version.  A real implementation  
  140.     may go against a database or LDAP server.  The id should remain "attributeRepository" though.  
  141.      -->  
  142.     <bean id="attributeRepository"  
  143.         class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">  
  144.         <constructor-arg index="0" ref="dataSource"/>  
  145.         <constructor-arg index="1" value="select * from academic_user where {0}"/>  
  146.         <property name="queryAttributeMapping">  
  147.             <map>  
  148.                 <entry key="username" value="username" />  
  149.             </map>  
  150.         </property>  
  151.         <property name="resultAttributeMapping">  
  152.             <map>  
  153.                 <entry key="username" value="username"/>  
  154.                 <entry key="name" value="name"/>  
  155.                 <entry key="email" value="email"/>  
  156.             </map>  
  157.         </property>  
  158.     </bean>  
  159.       
  160.     <!--   
  161.     Sample, in-memory data store for the ServiceRegistry. A real implementation  
  162.     would probably want to replace this with the JPA-backed ServiceRegistry DAO  
  163.     The name of this bean should remain "serviceRegistryDao".  
  164.      -->  
  165.     <bean  
  166.         id="serviceRegistryDao"  
  167.         class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl">  
  168.         <!--  
  169.             <property name="registeredServices">  
  170.                 <list>  
  171.                     <bean class="org.jasig.cas.services.RegisteredServiceImpl">  
  172.                         <property name="id" value="0" />  
  173.                         <property name="name" value="HTTP" />  
  174.                         <property name="description" value="Only Allows HTTP Urls" />  
  175.                         <property name="serviceId" value="http://**" />  
  176.                     </bean>  
  177.   
  178.                     <bean class="org.jasig.cas.services.RegisteredServiceImpl">  
  179.                         <property name="id" value="1" />  
  180.                         <property name="name" value="HTTPS" />  
  181.                         <property name="description" value="Only Allows HTTPS Urls" />  
  182.                         <property name="serviceId" value="https://**" />  
  183.                     </bean>  
  184.   
  185.                     <bean class="org.jasig.cas.services.RegisteredServiceImpl">  
  186.                         <property name="id" value="2" />  
  187.                         <property name="name" value="IMAPS" />  
  188.                         <property name="description" value="Only Allows HTTPS Urls" />  
  189.                         <property name="serviceId" value="imaps://**" />  
  190.                     </bean>  
  191.   
  192.                     <bean class="org.jasig.cas.services.RegisteredServiceImpl">  
  193.                         <property name="id" value="3" />  
  194.                         <property name="name" value="IMAP" />  
  195.                         <property name="description" value="Only Allows IMAP Urls" />  
  196.                         <property name="serviceId" value="imap://**" />  
  197.                     </bean>  
  198.                 </list>  
  199.             </property>  
  200.             -->  
  201.         </bean>  
  202.   
  203.     <bean id="auditTrailManager" class="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" />  
  204. </beans>  

注意上面一些我註釋掉的地方和添加的地方,我就不一一指出了,有什麼問題可以私下再問我。

在客戶端使用cas的時候,需要把cas-client的包導入web project/WEB-INF/lib裏,需要什麼包就用maven去打包特定的包。最關鍵的是web.xml文件裏對於filter的一些設定。在這些設定裏包括了cas的login和logout這倆最基礎的功能,還有一個很重要的是cas的validation。如果validation成功,cas會在session裏返回用戶名,而我在上面的xml裏還加入了別的用戶信息,這些東西會在validation成功之後寫入session裏,以xml的形式放着,我們可以用自己寫的AutoSetUserAdapterFilter來得到。下面是web.xml的配置,

[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  5.     id="WebApp_ID" version="2.5">  
  6.     <display-name>AcademicSearchEngine</display-name>  
  7.     <welcome-file-list>  
  8.         <welcome-file>home.jsp</welcome-file>  
  9.     </welcome-file-list>  
  10.   
  11.     <filter>  
  12.         <filter-name>struts2</filter-name>  
  13.         <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  14.     </filter>  
  15.   
  16.     <filter-mapping>  
  17.         <filter-name>struts2</filter-name>  
  18.         <url-pattern>/*</url-pattern>  
  19.     </filter-mapping>  
  20.   
  21.     <!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置 -->  
  22.     <listener>  
  23.         <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>  
  24.     </listener>  
  25.   
  26.     <!-- 該過濾器用於實現單點登出功能,可選配置。 -->  
  27.     <filter>  
  28.         <filter-name>CAS Single Sign Out Filter</filter-name>  
  29.         <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>  
  30.     </filter>  
  31.     <filter-mapping>  
  32.         <filter-name>CAS Single Sign Out Filter</filter-name>  
  33.         <url-pattern>/share.jsp</url-pattern>  
  34.     </filter-mapping>  
  35.   
  36.     <!-- 該過濾器負責用戶的認證工作,必須啓用它 -->  
  37.     <filter>  
  38.         <filter-name>CAS Authentication Filter</filter-name>  
  39.         <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>  
  40.         <init-param>  
  41.             <param-name>casServerLoginUrl</param-name>  
  42.             <param-value>http://dcd.academic:8443/cas/login</param-value>  
  43.         </init-param>  
  44.         <init-param>  
  45.             <!--這裏的server是服務端的IP -->  
  46.             <param-name>serverName</param-name>  
  47.             <param-value>http://dcd.academic:8080</param-value>  
  48.         </init-param>  
  49.         <init-param>  
  50.             <param-name>renew</param-name>  
  51.             <param-value>false</param-value>  
  52.         </init-param>  
  53.         <init-param>  
  54.             <param-name>gateway</param-name>  
  55.             <param-value>false</param-value>  
  56.         </init-param>  
  57.     </filter>  
  58.     <filter-mapping>  
  59.         <filter-name>CAS Authentication Filter</filter-name>  
  60.         <url-pattern>/share.jsp</url-pattern>  
  61.     </filter-mapping>  
  62.   
  63.   
  64.     <filter>  
  65.         <filter-name>CAS Validation Filter</filter-name>  
  66.         <filter-class>  
  67.             org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter  
  68.         </filter-class>  
  69.         <init-param>  
  70.             <param-name>casServerUrlPrefix</param-name>  
  71.             <param-value>http://dcd.academic:8443/cas</param-value>  
  72.         </init-param>  
  73.         <init-param>  
  74.             <param-name>serverName</param-name>  
  75.             <param-value>http://dcd.academic:8080</param-value>  
  76.         </init-param>  
  77.         <init-param>  
  78.             <param-name>useSession</param-name>  
  79.             <param-value>true</param-value>  
  80.         </init-param>  
  81.         <init-param>  
  82.             <param-name>redirectAfterValidation</param-name>  
  83.             <param-value>true</param-value>  
  84.         </init-param>  
  85.     </filter>  
  86.     <filter-mapping>  
  87.         <filter-name>CAS Validation Filter</filter-name>  
  88.         <url-pattern>/share.jsp</url-pattern>  
  89.     </filter-mapping>  
  90.   
  91.     <!-- 該過濾器負責實現HttpServletRequest請求的包裹, 比如允許開發者通過HttpServletRequest 的 getRemoteUser()方法獲得SSO登錄用戶的登錄名,可選配置。 -->  
  92.     <filter>  
  93.         <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>  
  94.         <filter-class>  
  95.             org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>  
  96.     </filter>  
  97.     <filter-mapping>  
  98.         <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>  
  99.         <url-pattern>/share.jsp</url-pattern>  
  100.     </filter-mapping>  
  101.   
  102.     <!-- 該過濾器使得開發者可以通過org.jasig.cas.client.util.AssertionHolder來獲取用戶的登錄名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 -->  
  103.     <filter>  
  104.         <filter-name>CAS Assertion Thread Local Filter</filter-name>  
  105.         <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>  
  106.     </filter>  
  107.     <filter-mapping>  
  108.         <filter-name>CAS Assertion Thread Local Filter</filter-name>  
  109.         <url-pattern>/share.jsp</url-pattern>  
  110.     </filter-mapping>  
  111.   
  112.     <!-- 自動根據單點登錄的結果設置本系統的用戶信息 -->  
  113.     <filter>  
  114.         <display-name>AutoSetUserAdapterFilter</display-name>  
  115.         <filter-name>AutoSetUserAdapterFilter</filter-name>  
  116.         <filter-class>dcd.academic.cas.AutoSetUserAdapterFilter</filter-class>  
  117.     </filter>  
  118.     <filter-mapping>  
  119.         <filter-name>AutoSetUserAdapterFilter</filter-name>  
  120.         <url-pattern>/share.jsp</url-pattern>  
  121.     </filter-mapping>  
  122. </web-app>  
每一個都很重要,我把https關掉了,所以serverName裏寫http就行。自己寫的AutoSetUserAdapterFilter來獲取session裏的用戶信息的代碼:

[java] view plain copy
  1. package dcd.academic.cas;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Map;  
  5.   
  6. import javax.servlet.Filter;  
  7. import javax.servlet.FilterChain;  
  8. import javax.servlet.FilterConfig;  
  9. import javax.servlet.ServletException;  
  10. import javax.servlet.ServletRequest;  
  11. import javax.servlet.ServletResponse;  
  12. import javax.servlet.http.HttpServletRequest;  
  13.   
  14. import org.jasig.cas.client.util.AssertionHolder;  
  15. import org.jasig.cas.client.validation.Assertion;  
  16.   
  17. import dcd.academic.DAO.DAOfactory;  
  18. import dcd.academic.DAO.UserDAO;  
  19. import dcd.academic.model.User;  
  20. import dcd.academic.util.StdOutUtil;  
  21.   
  22. /** 
  23.  * CAS單點登陸的過濾器功能類,該類用來自動生成子應用的登陸Session 
  24.  *  
  25.  */  
  26. public class AutoSetUserAdapterFilter implements Filter {  
  27.   
  28.     /** 
  29.      * Default constructor. 
  30.      */  
  31.     public AutoSetUserAdapterFilter() {  
  32.         StdOutUtil.out("[AutoSetUserAdapterFilter]");  
  33.     }  
  34.       
  35.   
  36.     /** 
  37.      * @see Filter#destroy() 
  38.      */  
  39.     public void destroy() {  
  40.     }  
  41.   
  42.     public void doFilter(ServletRequest request, ServletResponse response,  
  43.             FilterChain chain) throws IOException, ServletException {  
  44.         HttpServletRequest httpRequest = (HttpServletRequest) request;  
  45.           
  46.         // _const_cas_assertion_是CAS中存放登錄用戶名的session標誌  
  47.         Object object = httpRequest.getSession().getAttribute(  
  48.                 "_const_cas_assertion_");  
  49.       
  50.         if (object != null) {  
  51.             Assertion assertion = (Assertion) object;  
  52.             String loginName = assertion.getPrincipal().getName();  
  53.             StdOutUtil.out("[loginname]: " + loginName);  
  54.               
  55.             Map<String, Object> map = assertion.getPrincipal().getAttributes();  
  56.             String email = (String) map.get("email");  
  57.             String name = (String) map.get("name");  
  58.             String username = (String) map.get("username");  
  59.             StdOutUtil.out("[email]: " + email);  
  60.             StdOutUtil.out("[name]: " + name);  
  61.             StdOutUtil.out("[username]: " + username);  
  62.         }  
  63.         chain.doFilter(request, response);  
  64.     }  
  65.   
  66.     /** 
  67.      * @see Filter#init(FilterConfig) 
  68.      */  
  69.     public void init(FilterConfig fConfig) throws ServletException {  
  70.     }  
  71.   
  72. }  

還有一點,就是在validation success的返回jsp裏,要新添加一些內容,在目錄cas\WEB-INF\view\jsp\protocol\2.0的casServiceValidationSuccess.jsp

[html] view plain copy
  1. <%@ 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" %>  
  2. <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>  
  3.     <cas:authenticationSuccess>  
  4.         <cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}</cas:user>  
  5.         <c:if test="${not empty pgtIou}">  
  6.             <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>  
  7.         </c:if>  
  8.         <c:if test="${fn:length(assertion.chainedAuthentications) > 1}">  
  9.             <cas:proxies>  
  10.                 <c:forEach var="proxy" items="${assertion.chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications)-2}" step="1">  
  11.                     <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>  
  12.                 </c:forEach>  
  13.             </cas:proxies>  
  14.         </c:if>  
  15.   
  16.         <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">  
  17.             <cas:attributes>  
  18.                 <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">  
  19.                     <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>  
  20.                 </c:forEach>  
  21.             </cas:attributes>  
  22.         </c:if>  
  23.     </cas:authenticationSuccess>  
  24. </cas:serviceResponse>  


其實本質上這些都是servlet的處理。因爲cas也是一個servlet寫成的war,說簡單也簡單。所以cas自己的登錄界面我們都是自己自己定製的。

我們在使用的時候,需要改動的項目代碼很少。在需要登錄或者認證的地方,把鏈接跳轉到server:8443/cas/login上,登錄成功後讓cas的登錄成功界面跳轉回原service的url即可,這時候cas是通過service和service ticket生成了新的ticket grant ticket,然後在session裏存了東西讓客戶端去讀取的。在安全方面,這步是在SSL的基礎上做的,所以我直接訪問如server:8443/cas/serviceValidation是會出SSL證書錯誤的。

還是稍微說一下cas的協議機制吧。這張圖也是別人文章裏的圖,爲了方便大家理解,還是帖一下。


•ST:Service Ticket,用於客戶端應用持有,每個ST對應一個用戶在一個客戶端上
•TGT:Ticket Granting Ticket,存儲在CAS服務器端和用戶cookie兩個地方
•CAS服務器持有STTGT+客戶端的映射關係,客戶端持有ST與用戶Session的映射關係,在renew的情況下,每次客戶端根據用戶Session將ST發送給CAS服務器端,服務器端檢驗ST是否存在即可知道此用戶是否已登陸。在普通情況下,用戶第一次登陸應用時,客戶端將用戶頁面重定向到CAS服務器,服務器取出用戶cookie中的TGT,檢驗是否在服務器中存在,若存在則生成ST返回給客戶端  (若不存在則要求登陸,登陸成功後同樣返回ST給客戶端),客戶端拿到ST後再發送給CAS服務器認證是否爲真實ST,認證成功即表示登陸成功

總結

總結cas的話,我們可以單獨給一個tomcat來做用戶認證模塊,並且認證之後,客戶端是可以得到session裏的用戶信息的。可以認爲這樣就把單點登錄問題解決了。至於這個cas服務器怎麼配置,怎麼認證,需要傳遞什麼的,就去tomcat/webapps/cas的許許多多jsp和xml裏去配置。話說這些jsp和xml真的很多。

像這樣的開源企業級解決方案,說簡單也簡單,說難也難,就和solr一樣。配置這件事,要進階使用的話需要很大力氣花在源碼閱讀上,這樣你纔可以很好的進行定製和擴展。不然我們無法知道他給你寫好的簡單配置和複雜配置是怎麼實現的,我們應該使用哪些寫好的handler,需要什麼params。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章