SSO單點登入系列6:cas單點登入防止登出退出後重新整理後退ticket失效報500錯
這個問題之前就發現過,最近有幾個哥們一直在問我這個怎麼搞,我手上在做另一個專案,cas就暫時擱淺了幾周。現在我們來一起改一下你的應用(client2/3)的web.xml來解決這個2b問題,首先看下錯誤描述:
問題: 我登入了client2,又登入了client3,現在我把client2退出了,在client3裡面我F5重新整理了一下,結果頁面報錯:
未能夠識別出目標 'ST-41-2VcnVMguCDWJX5zHaaaD-cas01.example.org'票根
type Exception report
message org.jasig.cas.client.validation.TicketValidationException:
description The server encountered an internal error that prevented it from fulfilling this request.
exception
javax.servlet.ServletException: org.jasig.cas.client.validation.TicketValidationException:
鏈兘澶熻瘑鍒嚭鐩爣 'ST-41-2VcnVMguCDWJX5zHaaaD-cas01.example.org'紲ㄦ牴
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:155)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:99)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
root cause
org.jasig.cas.client.validation.TicketValidationException:
鏈兘澶熻瘑鍒嚭鐩爣 'ST-41-2VcnVMguCDWJX5zHaaaD-cas01.example.org'紲ㄦ牴
org.jasig.cas.client.validation.Cas20ServiceTicketValidator.parseResponseFromServer(Cas20ServiceTicketValidator.java:73)
org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:188)
org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:132)
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:99)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.37 logs.
猜都能猜出來,我登出了,ticket已經失效了,現在我又發回到server端,它就報錯了。(客戶端發過去就報錯了),以下就是cas ticket失效處理的一個很簡單的解決辦法,複雜的話,需要修改client原始碼進行異常處理。
1.所以針對這個情況,我只能在web.xml中下手了,(你也可以修改客戶端的jar包中的一些java類,自己去做這個異常處理,接收所有在cas使用過程中會出錯的處理,全部跳轉到錯誤頁面中,讓掉線的人重新登入。在這裡,我們採用web.xml配置一下)
2.這是官網解釋:https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml 它的解釋:
The correct order of the filters in web.xml is necessary:
- AuthenticationFilter
- TicketValidationFilter (whichever one is chosen)
- HttpServletRequestWrapperFilter
- AssertionThreadLocalFilter
3.這是一個哥們之前解釋的:我貼出來。
單點登出,客戶端配置。我嘗試使用SAML作為認證和Ticket校驗,但是除錯時發現單點登出取標識的方式只能識別CAS的認證和校驗。
認證:org.jasig.cas.client.authentication.AuthenticationFilter
校驗:org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
過濾器順序:
1. CAS Single Sign Out Filter
2. CAS Validation Filter
3. CAS Authentication Filter
4. CAS HttpServletRequest Wrapper Filter
5. CAS Assertion Thread Local Filter
特別注意Validation在Authentication之前,因為我使用的是Cas20ProxyReceivingTicketValidationFilter。根據CAS文件描述:If you are using proxy validation, you should map the validation filter before the authentication filter.
4.ok,放上我的web.xml檔案,廢掉之前的cas驗證過濾器(CAS Filter)。使用另一個過濾器(CAS Authentication Filter),並且增加另外三個過濾器(CAS Validation Filter,CAS HttpServletRequest Wrapper Filter,CAS Assertion Thread Local Filter),注意過濾器的順序.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 解決中文亂碼問題 -->
<filter>
<filter-name>spring filter</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>
</filter>
<filter-mapping>
<filter-name>spring filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 解決中文亂碼問題 -->
<!--1.用於單點退出 -->
<listener>
<listener-class>
org.jasig.cas.client.session.SingleSignOutHttpSessionListener
</listener-class>
</listener>
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>
org.jasig.cas.client.session.SingleSignOutFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--2.負責Ticket校驗-->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>
http://192.168.168.141:8080/casServer
</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>192.168.168.141:8080</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>exceptionOnValidationFailure</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 3. 單點登入驗證 -->
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>
org.jasig.cas.client.authentication.AuthenticationFilter
</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>
http://192.168.168.141:8080/casServer/login
</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://192.168.168.141:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 3.用於單點登入 去伺服器端認證(之前使用的這種)
<filter>
<filter-name>CAS Filter</filter-name>
<filter-class>
edu.yale.its.tp.cas.client.filter.CASFilter
</filter-class>
<init-param>
<param-name>
edu.yale.its.tp.cas.client.filter.loginUrl
</param-name>
<param-value>
http://192.168.168.141:8080/casServer/login
</param-value>
</init-param>
<init-param>
<param-name>
edu.yale.its.tp.cas.client.filter.validateUrl
</param-name>
<param-value>
http://192.168.168.141:8080/casServer/serviceValidate
</param-value>
</init-param>
<init-param>
<param-name>
edu.yale.its.tp.cas.client.filter.serverName
</param-name>
<param-value>192.168.168.141:8080</param-value>
</init-param>
</filter>
-->
<!--4. CAS HttpServletRequest Wrapper Filter 這個是HttpServletRequet的包裹類,讓他支援getUserPrincipal,getRemoteUser方法來取得使用者資訊-->
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--5. CAS Assertion Thread Local Filter 這個類把Assertion資訊放在ThreadLocal變數中,這樣應用程式不在web層也能夠獲取到當前登入資訊-->
<filter>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.AssertionThreadLocalFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Query</servlet-name>
<servlet-class>servlet.Query</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Query</servlet-name>
<url-pattern>/query</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
5.如果這樣做了,你還需要一件事情,就是前臺獲取使用者資訊的方式改了,我的index.jsp改成了這個:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@page import="edu.yale.its.tp.cas.client.filter.CASFilter"%>
<%@page import="org.jasig.cas.client.util.AssertionThreadLocalFilter"%>
<%@page import="org.jasig.cas.client.util.HttpServletRequestWrapperFilter"%>
<%@page import="org.jasig.cas.client.authentication.AttributePrincipal"%>
<%@page import="org.jasig.cas.client.util.AbstractCasFilter"%>
<%@page import="org.jasig.cas.client.validation.Assertion"%>
<body>
<h1>
登入成功,這是客戶端2
</h1>
<br />
歡迎您:
<%
//String username = (String) session.getAttribute(CASFilter.CAS_FILTER_USER);
//String username2 = (String)AssertionHolder.getAssertion().getPrincipal().getName();
String username = "";
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
if(principal != null){
username = principal.getName();//獲取使用者名稱
}
%>
使用者名稱:<%=username%>
ok,我的應用之間如果一個退出,另一個就算帶ticket引數也不不再報錯了,就算是測試組的兄弟拿到那段ticket複製貼上到另一個瀏覽器中進行訪問,也不會報錯。
ps:
也有兄弟說可以通過修改C:\tomcat7\webapps\casServer\WEB-INF\spring-configuration\ticketExpirationPolicies.xml這個檔案中的
<!-- Expiration policies -->
<util:constant id="SECONDS" static-field="java.util.concurrent.TimeUnit.SECONDS"/>
<bean id="serviceTicketExpirationPolicy" class="org.jasig.cas.ticket.support.MultiTimeUseOrTimeoutExpirationPolicy"
c:numberOfUses="1" c:timeToKill="${st.timeToKillInSeconds:10}" c:timeUnit-ref="SECONDS"/>
其中那個
c:numberOfUses="1" //使用ticket多少次
c:timeToKill="${st.timeToKillInSeconds:10}" //多少秒過期,預設10秒,你把這個改成10分鐘玩玩。
落雨
2013年7月26日13:37:05