【試水CAS-4.0.3】第07節_CAS客戶端配置單點登入
阿新 • • 發佈:2019-01-02
完整版見https://jadyer.github.io/2015/07/26/sso-cas-client-login/
現在開始測試
/** * @see CAS客戶端配置 * @see ------------------------------------------------------------------------------------------------------------------------ * @see 這裡用的是cas-client-core-3.4.0.jar(這是2015-07-21釋出的) * @see 下載地址http://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core/3.4.0 * @see 另外為了使客戶端在HTTP協議下單點成功,可以修改以下兩處配置使其不開啟HTTPS驗證 * @see 1.\WEB-INF\deployerConfigContext.xml * @see <bean class="org.jasig...support.HttpBasedServiceCredentialsAuthenticationHandler">新增p:requireSecure="false" * @see 2.\WEB-INF\spring-configuration\ticketGrantingTicketCookieGenerator.xml和\WEB-INF\spring-configuration\warnCookieGenerator.xml * @see p:cookieSecure="true"改為p:cookieSecure="false" * @see 下面介紹兩種配置方法,一種是純web.xml配置,一種是藉助Spring來配置,相關的官方文件如下所示 * @see https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml * @see https://wiki.jasig.org/display/CASC/Configuring+the+JA-SIG+CAS+Client+for+Java+using+Spring * @see ------------------------------------------------------------------------------------------------------------------------ * @see 純web.xml * @see web.xml中需配置四個順序固定的Filter,而且出於認證考慮,最好配置在其他Filter之前,它們的先後順序如下 * @see AuthenticationFilter * @see TicketValidationFilter(或其它AbstractTicketValidationFilter實現,比如Cas20ProxyReceivingTicketValidationFilter) * @see HttpServletRequestWrapperFilter * @see AssertionThreadLocalFilter * @see 另外各個Filter的<init-param>優先順序都比<context-param>要高,通常<context-param>用來配置公用的引數 * @see 1.AuthenticationFilter * @see 用來攔截請求,判斷是否需要CASServer認證,需要則跳轉到CASServer登入頁,否則放行請求 * @see 有兩個必須引數,一個是指定CASServer登入地址的casServerLoginUrl,另一個是指定認證成功後跳轉地址的serverName或service * @see service和serverName設定一個即可,二者都設定時service的優先順序更高,即會以service為準 * @see service指的是一個確切的URL,而serverName是用來指定客戶端的主機名的,格式為{protocol}:{hostName}:{port} * @see 指定serverName時,該Filter會把它附加上當前請求的URI及對應的查詢引數來構造一個確切的URL作為認證成功後的跳轉地址 * @see 比如serverName為"http://gg.cn",當前請求的URI為"/oa",查詢引數為"aa=bb",則認證成功後跳轉地址為http://gg.cn/oa?aa=bb * @see casServerLoginUrl--去哪登入,serverName--我是誰 * @see 2.TicketValidationFilter * @see 請求通過AuthenticationFilter認證後,若請求中攜帶了ticket引數,則會由該類Filter對攜帶的ticket進行校驗 * @see 驗證ticket的時候,要訪問CAS服務的/serviceValidate介面,使用的url就是${casServerUrlPrefix}/serviceValidate * @see 所以它也有兩個引數是必須指定的,casServerUrlPrefix(CASServer對應URL地址的字首)和serverName或service * @see 實際上,TicketValidationFilter只是對驗證ticket的這一類Filter的統稱,其並不對應CASClient中的具體型別 * @see CASClient中有多種驗證ticket的Filter,都繼承自AbstractTicketValidationFilter * @see 常見的有Cas10TicketValidationFilter/Cas20ProxyReceivingTicketValidationFilter/Saml11TicketValidationFilter * @see 它們的驗證邏輯都是一致的,都有AbstractTicketValidationFilter實現,只是使用的TicketValidator不一樣而已 * @see 如果要從伺服器獲取使用者名稱之外的更多資訊應該採用CAS20這個2.0協議的代理 * @see 3.HttpServletRequestWrapperFilter * @see 用於封裝每個請求的HttpServletRequest為其內部定義的CasHttpServletRequestWrapper * @see 它會將儲存在Session或request中的Assertion物件重寫HttpServletRequest的getUserPrincipal()、getRemoteUser()、isUserInRole() * @see 這樣在我們的應用中就可以非常方便的從HttpServletRequest中獲取到使用者的相關資訊 * @see 4.AssertionThreadLocalFilter * @see 為了方便使用者在應用的其它地方獲取Assertion物件,其會將當前的Assertion物件存放到當前的執行緒變數中 * @see 以後使用者在程式的任何地方都可以從執行緒變數中獲取當前的Assertion,而無需從Session或request中解析 * @see 該執行緒變數是由AssertionHolder持有的,我們在獲取當前的Assertion時也只需Assertion assertion = AssertionHolder.getAssertion() * @see ------------------------------------------------------------------------------------------------------------------------ * @see 藉助Spring * @see 與上述web.xml配置四個Filter方式不同的是,可以使用Spring的四個DelegatingFilterProxy來代理需要配置的四個Filter * @see 此時這四個Filter就應該配置為Spring的Bean物件,並且web.xml中的<filter-name>就應該對應SpringBean名稱 * @see 但是SingleSignOutFilter/HttpServletRequestWrapperFilter/AssertionThreadLocalFilter等Filter不含配置引數 * @see 所以實際上只需要配置AuthenticationFilter和Cas20ProxyReceivingTicketValidationFilter兩個Filter交由Spring代理就可以了 * @see ------------------------------------------------------------------------------------------------------------------------ * @see 注意 * @see 1.CAS1.0提供的介面有/validate,CAS2.0提供的介面有/serviceValidate,/proxyValidate,/proxy * @see 2.四個Filter太多了,有時間的話考慮參考org.springframework.web.filter.CompositeFilter寫一個Filter來實現 * @see 3.web.xml的好處是可以配置匿名訪問的資源,配置引數參考AuthenticationFilter中的ignoreUrlPatternMatcherStrategyClass * @see 起碼cas-client-core-3.4.0.jar中的Spring配置還不支援ignorePattern(該引數預設正則驗證,此外還有contains和equals驗證) * @see 4.javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching casserver found * @see 這是由於建立證書的域名和應用中配置的CAS服務域名不一致導致出錯(說白了就是指客戶端匯入的CRT證書與CAS服務端的域名不同) * @see ------------------------------------------------------------------------------------------------------------------------ * @create 2015-7-26 下午1:00:14 * @author 玄玉<http://blog.csdn.net/jadyer> */
下面是web.xml的配置
下面是//src//applicationContext.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//config.properties<?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>
#<<Central Authentication Service>>
#where to login
casServerLoginUrl=http://sso.jadyer.com:8080/cas-server-web/login
#login server root
casServerUrlPrefix=http://sso.jadyer.com:8080/cas-server-web
#who am i
#casClientServerName=http://boss.jadyer.com:8180
casClientServerName=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/>");
}
%>
接下來就可以測試了,測試之前先修改幾處配置,模擬單點環境/**
* @see ------------------------------------------------------------------------------------------------------------------------
* @see 測試時在C:\Windows\System32\drivers\etc\hosts中新增以下三個配置
* @see 127.0.0.1 sso.jadyer.com
* @see 127.0.0.1 boss.jadyer.com
* @see 127.0.0.1 risk.jadyer.com
* @see ------------------------------------------------------------------------------------------------------------------------
* @see 然後拷貝三個Tomcat,分別用作sso伺服器和兩個sso客戶端
* @see 修改兩個sso客戶端的\Tomcat\conf\server.xml的以下三個埠,保證啟動監聽埠不重複
* @see <Server port="8105" shutdown="SHUTDOWN">
* @see <Connector port="8180" protocol="HTTP/1.1"......>
* @see <Connector port="8109" protocol="AJP/1.3" redirectPort="8443" />
* @see <Server port="8205" shutdown="SHUTDOWN">
* @see <Connector port="8280" protocol="HTTP/1.1"......>
* @see <Connector port="8209" protocol="AJP/1.3" redirectPort="8443" />
* @see ------------------------------------------------------------------------------------------------------------------------
* @see 最後修改兩個sso客戶端的\Tomcat\webapps\cas-client\WEB-INF\classes\config.properties的casClientServerName值
* @see casClientServerName=http://boss.jadyer.com:8180
* @see casClientServerName=http://risk.jadyer.com:8280
* @see ------------------------------------------------------------------------------------------------------------------------
* @create 2015-7-26 下午1:08:35
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
現在開始測試
先訪問http://boss.jadyer.com:8180/cas-client,發現沒登入會自動跳轉到單點登入頁
輸入密碼後登入成功
再訪問http://risk.jadyer.com:8280/cas-client,會發現自動登入成功,不用再登入了