單點登出功能跟單點登錄功能是相對應的,旨在通過Cas Server的登出使所有的Cas Client都登出。
Cas Server的登出是通過請求“/logout”發生的,即如果你的Cas Server部署的訪問路徑為“https://localhost:8443/cas”時,
通過訪問“https://localhost:8443/cas/logout”可以觸發Cas Server的登出操作,進而觸發Cas Client的登出。
在請求Cas Server的logout時,Cas Server會將客戶端攜帶的TGC刪除,同時回調該TGT對應的所有service,即所有的Cas Client。
Cas Client如果需要響應該回調,進而在Cas Client端進行登出操作的話就需要有對應的支持。
具體來說,需要在Cas Client應用的web.xml文件中添加如下Filter和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> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener>
我們分別看一下這兩個類的源碼:
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { // 轉換參數
final HttpServletRequest request = (HttpServletRequest) servletRequest; //判斷參數中是否具有artifactParameterName屬性指定的參數名稱,默認是ticket
if (handler.isTokenRequest(request)) { // 如果存在,在本地sessionMappingStorage中記錄session。
handler.recordSession(request); } else if (handler.isLogoutRequest(request)) {//判斷是否具有logoutParameterName參數指定的參數,默認參數名稱為logoutRequest // 如果存在,則在sessionMappingStorage中刪除記錄,並注銷session。
handler.destroySession(request); // 注銷session后,立刻停止執行后面的過濾器
return; } else { log.trace("Ignoring URI " + request.getRequestURI()); } //條件都不滿足,繼續執行下面的過濾器
filterChain.doFilter(servletRequest, servletResponse); }
用戶通過瀏覽器訪問系統A www.a.com/pageA
,這個 pageA
是個需要登錄才能訪問的頁面,系統A發現用戶沒有登錄,這時候系統A需要做一個額外的操作,就是重定向到認證中心: www.sso.com/login?service=www.a.com/pageA
。
這個 service
參數是一個回跳的url,認證完成后會重定向到系統A,另外有一個作用就是注冊服務,簡單來說注冊服務為的是讓我們的認證中心能夠知道有哪些系統在我們這里完成過登錄,其中一個重要目的是為了完成單點退出的功能。
注冊服務時保存一是回跳的service地址,二是對應的ticket。
拿着ticket回跳到客戶端后,客戶端一看地址的請求參數有ticket的時候,singleSignOutFilter注冊帶有ticket作為id的session到sessionMappingStorage。
當用戶訪問認證中心的 /logout
需要退出的時候,認證中心先把TGT干掉,然后給之前注冊過那些服務的地址發送退出登錄的請求,並且攜帶之前登錄的ticket,客戶端一看請求中參數有logoutRequest的時候,
客戶端的singleSignOutFilter根據傳過來的這個 ticket
來將對應的用戶 session
干掉即可。
那么什么時候干掉sessionMappingStorage呢?(sessionMappingStorage是一個map,key為ticket,value為session)
這是靠SingleSignOutHttpSessionListener來實現的,當有session被注銷的時候,觸發將sessionMappingStorage中對應的sessionId中的數據刪除,
所以在配置單點登出的時候,一定要配置這個監聽器,否則客戶端很容易導致內存溢出。
SingleSignOutHttpSessionListener 源碼:
/** * Listener to detect when an HTTP session is destroyed and remove it from the map of * managed sessions. Also allows for the programmatic removal of sessions. * <p> * Enables the CAS Single Sign out feature. * * Scott Battaglia * @version $Revision$ Date$ * @since 3.1 */
public final class SingleSignOutHttpSessionListener implements HttpSessionListener { private SessionMappingStorage sessionMappingStorage; public void sessionCreated(final HttpSessionEvent event) { // nothing to do at the moment
} public void sessionDestroyed(final HttpSessionEvent event) { if (sessionMappingStorage == null) { sessionMappingStorage = getSessionMappingStorage(); } final HttpSession session = event.getSession(); sessionMappingStorage.removeBySessionById(session.getId()); } /** * Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same * instance of the object. It assumes this because it generally lazily calls the method. * * @return the SessionMappingStorage */
protected static SessionMappingStorage getSessionMappingStorage() { return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage(); } }