這里用的是:cas-client-core-3.4.0.jar(2015-07-21發布的)
下載地址為:http://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core/3.4.0
下面介紹兩種配置方法:一種是純web.xml配置,一種是借助SPRing來配置,相關的官方文檔如下所示
https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+java+in+the+web.xml
https://wiki.jasig.org/display/CASC/Configuring+the+JA-SIG+CAS+Client+for+Java+using+Spring
web.xml中需配置四個順序固定的Filter,而且出于認證考慮,最好配置在其他Filter之前,它們的先后順序如下
AuthenticationFilterTicketValidationFilter(或其它AbstractTicketValidationFilter實現,比如Cas20ProxyReceivingTicketValidationFilter)HttpServletRequestWrapperFilterAssertionThreadLocalFilter下面分別介紹一下四個Filter的各自用途
AuthenticationFilter用來攔截請求,判斷是否需要CASServer認證,需要則跳轉到CASServer登錄頁,否則放行請求
有兩個必須參數:一個是指定CASServer登錄地址的casServerLoginUrl,一個是指定認證成功后跳轉地址的serverName或service
service和serverName設置一個即可,二者都設置時service的優先級更高(即會以service為準)
service指的是一個確切的URL,而serverName是用來指定客戶端的主機名的,格式為{protocol}:{hostName}:{port}
指定serverName時,該Filter會把它附加上當前請求的URI及對應的查詢參數來構造一個確切的URL作為認證成功后的跳轉地址
比如serverName=”http://gg.cn”,當前請求的URI為”/oa”,查詢參數為”aa=bb”,則認證成功后跳轉地址為http://gg.cn/oa?aa=bb
簡單來講:casServerLoginUrl--去哪登錄,serverName--我是誰
請求通過AuthenticationFilter認證后,若請求中攜帶了ticket參數,則會由該類Filter對攜帶的ticket進行校驗
驗證ticket的時候,要訪問CAS服務的/serviceValidate接口,使用的url就是${casServerUrlPrefix}/serviceValidate
所以它也有兩個參數是必須指定的,casServerUrlPrefix(CASServer對應URL地址的前綴)和serverName或service
實際上,TicketValidationFilter只是對驗證ticket的這一類Filter的統稱,其并不對應CASClient中的具體類型
CASClient中有多種驗證ticket的Filter,都繼承自AbstractTicketValidationFilter
常見的有Cas10TicketValidationFilter/Cas20ProxyReceivingTicketValidationFilter/Saml11TicketValidationFilter
它們的驗證邏輯都是一致的,都有AbstractTicketValidationFilter實現,只是使用的TicketValidator不一樣而已
如果要從服務器獲取用戶名之外的更多信息應該采用CAS20這個2.0協議的代理
HttpServletRequestWrapperFilter用于封裝每個請求的HttpServletRequest為其內部定義的CasHttpServletRequestWrapper
它會將保存在session或request中的Assertion對象重寫HttpServletRequest的getUserPrincipal()、getRemoteUser()、isUserInRole()
這樣在我們的應用中就可以非常方便的從HttpServletRequest中獲取到用戶的相關信息
AssertionThreadLocalFilter為了方便用戶在應用的其它地方獲取Assertion對象,其會將當前的Assertion對象存放到當前的線程變量中
以后用戶在程序的任何地方都可以從線程變量中獲取當前的Assertion,而無需從Session或request中解析
該線程變量是由AssertionHolder持有的,可以通過Assertion assertion = AssertionHolder.getAssertion()獲取當前Assertion
與上述web.xml配置四個Filter方式不同的是,可以使用Spring的四個DelegatingFilterProxy來代理需要配置的四個Filter
此時這四個Filter就應該配置為Spring的Bean對象,并且web.xml中的就應該對應SpringBean名稱
但是SingleSignOutFilter/HttpServletRequestWrapperFilter/AssertionThreadLocalFilter等Filter不含配置參數
所以實際上只需要配置AuthenticationFilter和Cas20ProxyReceivingTicketValidationFilter兩個Filter交由Spring代理就可以了
CAS1.0提供的接口有/validate,CAS2.0提供的接口有/serviceValidate、/proxyValidate、/proxy
四個Filter太多了,有時間的話考慮參考org.springframework.web.filter.CompositeFilter寫一個Filter來實現
web.xml的好處是可以配置匿名訪問的資源,配置參數參考AuthenticationFilter中的ignoreUrlPatternMatcherStrategyClass
起碼cas-client-core-3.4.0.jar中的Spring配置還不支持ignorePattern(該參數默認正則驗證,此外還有contains和equals驗證)
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching casserver found這是由于創建證書的域名和應用中配置的CAS服務域名不一致(也就是說客戶端導入的CRT證書與CAS服務端的域名不同)
本文源碼下載:(下面兩個地址的文件的內容,都是一樣的)
http://oirr30q6q.bkt.clouddn.com/jadyer/code/sso-cas-client-login.rar
http://download.csdn.net/detail/jadyer/8934207
下面是web.xml的配置
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- SSO --> <filter> <filter-name>casAuthenticationFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>casAuthenticationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>casTicketValidationFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>casTicketValidationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- <context-param> <param-name>serverName</param-name> <param-value>http://boss.jadyer.com:8080</param-value> </context-param> <filter> <filter-name>casAuthenticationFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>http://sso.jadyer.com:8080/cas-server-web/login</param-value> </init-param> </filter> <filter-mapping> <filter-name>casAuthenticationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>casTicketValidationFilter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://sso.jadyer.com:8080/cas-server-web</param-value> </init-param> </filter> <filter-mapping> <filter-name>casTicketValidationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> --> <filter> <filter-name>casHttpServletRequestWrapperFilter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>casHttpServletRequestWrapperFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>casAssertionThreadLocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>casAssertionThreadLocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app>下面是/src/applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/> <property name="ignoreResourceNotFound" value="false"/> <property name="locations"> <list> <value>classpath:config.properties</value> </list> </property> </bean> <mvc:resources mapping="/index.jsp" location="/index.jsp"/> <!-- cas --> <bean name="casAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter"> <property name="serverName" value="${casClientServerName}"/> <property name="casServerLoginUrl" value="${casServerLoginUrl}"/> </bean> <bean name="casTicketValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter"> <property name="serverName" value="${casClientServerName}"/> <property name="ticketValidator"> <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"> <constructor-arg index="0" value="${casServerUrlPrefix}"/> </bean> </property> </bean></beans>下面是/src/config.properties
#<<Central Authentication Service>>#where to logincasServerLoginUrl=http://sso.jadyer.com:8080/cas-server-web/login#login server rootcasServerUrlPrefix=http://sso.jadyer.com:8080/cas-server-web#who am i#casClientServerName=http://boss.jadyer.com:8180casClientServerName=http://risk.jadyer.com:8280最后是/WebRoot/index.jsp
<%@ page pageEncoding="UTF-8"%><%@ page import="java.util.Map"%><%@ page import="java.net.URLDecoder"%><%@ page import="org.jasig.cas.client.util.AssertionHolder"%><%@ page import="org.jasig.cas.client.authentication.AttributePrincipal"%><body style="background-color:#CBE0C9;"> <span style="color:red; font-size:32px; font-weight:bold;">客戶端登錄成功</span></body><hr size="2"><% AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal(); Map<String, Object> attributes = principal.getAttributes(); out.print("principal.getName()=" + principal.getName() + "<br/>"); out.print("request.getRemoteUser()=" + request.getRemoteUser() + "<br/>"); out.print("登錄用戶:" + attributes.get("userId") + "<br/>"); out.print("登錄時間:" + AssertionHolder.getAssertion().getAuthenticationDate() + "<br/>"); out.print("-----------------------------------------------------------------------<br/>"); for(Map.Entry<String, Object> entry : attributes.entrySet()){ //服務端返回中文時需要encode,客戶端接收顯示中文時需要decode,否則會亂碼 out.print(entry.getKey() + "=" + URLDecoder.decode(entry.getValue().toString(), "UTF-8") + "<br/>"); } out.print("-----------------------------------------------------------------------<br/>"); Map<String, Object> attributes22 = AssertionHolder.getAssertion().getAttributes(); for(Map.Entry<String, Object> entry : attributes22.entrySet()){ out.print(entry.getKey() + "=" + entry.getValue() + "<br/>"); } out.print("-----------------------------------------------------------------------<br/>"); Map<String, Object> attributes33 = AssertionHolder.getAssertion().getPrincipal().getAttributes(); for(Map.Entry<String, Object> entry : attributes33.entrySet()){ out.print(entry.getKey() + "=" + entry.getValue() + "<br/>"); }%>測試
接下來就可以測試了,測試之前先修改幾處配置,模擬單點環境
首先在C:/Windows/System32/drivers/etc/hosts中添加以下三個配置
127.0.0.1 sso.jadyer.com127.0.0.1 boss.jadyer.com127.0.0.1 risk.jadyer.com然后拷貝三個Tomcat,分別用作sso服務器和兩個sso客戶端
修改兩個sso客戶端的/Tomcat/conf/server.xml的以下三個端口,保證啟動監聽端口不重復
<Server port="8105" shutdown="SHUTDOWN"><Connector port="8180" protocol="HTTP/1.1"......><Connector port="8109" protocol="AJP/1.3" redirectPort="8443"/><Server port="8205" shutdown="SHUTDOWN"><Connector port="8280" protocol="HTTP/1.1"......><Connector port="8209" protocol="AJP/1.3" redirectPort="8443"/>最后修改兩個sso客戶端/Tomcat/webapps/cas-client/WEB-INF/classes/config.properties里面的casClientServerName的值
casClientServerName=http://boss.jadyer.com:8180casClientServerName=http://risk.jadyer.com:8280下面開始測試
先訪問http://boss.jadyer.com:8180/cas-client,發現沒登錄會自動跳轉到單點登錄頁

輸入密碼后登錄成功

再訪問http://risk.jadyer.com:8280/cas-client,會發現自動登錄成功,不用再登錄了

新聞熱點
疑難解答