使用shiro集成CAS實現(xiàn)單點登錄的文章有很多,配置大同小異。與之對應的單點登出可能大家關注的不夠。
單點登出,表示瀏覽器同時訪問了多個接入單點登錄系統(tǒng),在某個系統(tǒng)點擊退出的同時,其他系統(tǒng)也應該同時登出。進一步提升了系統(tǒng)的安全性。
CAS,提供了很好的單點登出實現(xiàn),用戶只需要簡單配置對應的監(jiān)聽器和過濾即可。原理也很簡單,網上有很多說明。
<!-- 單點登出監(jiān)聽器 --> <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>
線上會遇到一個問題:用戶登錄后權限信息發(fā)生變化。shiro默認會將用戶權限信息、系統(tǒng)權限信息進行緩存,一定程度上提升系統(tǒng)的響應。使用默認單點退出,并不會將用戶緩存信息進行情況。
基于這個問題,對SingleSignOutFilter進行一定改造:
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; if (handler.isTokenRequest(request)) { handler.recordSession(request); } else if (handler.isLogoutRequest(request)) { handler.destroySession(request); // Do not continue up filter chain return; } else { log.trace("Ignoring URI " + request.getRequestURI()); } filterChain.doFilter(servletRequest, servletResponse); }
解讀源碼,handler.destroySession(request),
public void destroySession(final HttpServletRequest request) { final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName); if (log.isTraceEnabled()) { log.trace ("Logout request:\n" + logoutMessage); } final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex"); if (CommonUtils.isNotBlank(token)) { final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token); if (session != null) { String sessionID = session.getId(); if (log.isDebugEnabled()) { log.debug ("Invalidating session [" + sessionID + "] for token [" + token + "]"); } try { session.invalidate(); } catch (final IllegalStateException e) { log.debug("Error invalidating session.", e); } } } }
代碼會解析,獲取當前登錄用戶的session,有了session,我們就可以得到shiro中的Subject對象了,就能調用subject.logout()方法,清除緩存了。當用戶退出重新登錄系統(tǒng),新的權限信息立馬生
if (handler.isTokenRequest(request)) { handler.recordSession(request); } else if (handler.isLogoutRequest(request)) { HttpSession session = handler.getSession(request); log.info("single sign out request,SesionID[" +session.getId()+"]"); if(session != null){ new WebSubject.Builder(getSecurityManager(), request, response).session(new HttpServletSession(session,"")).buildSubject().logout(); log.info("single sign out request,SesionID[" +session.getId()+"]"+",shiro subject logout success!"); } handler.destroySession(session); log.info("single sign out request,SesionID[" +session.getId()+"]"+",destroy session success!"); // Do not continue up filter chain return; } else { log.trace("Ignoring URI " + request.getRequestURI()); } filterChain.doFilter(servletRequest, servletResponse);
重寫了SingleSignOutHandler類,首先獲取session,然后通過new WebSubject。親測有效。