1 Single Sign Out 功能
即單點登出功能。也就是在任意子系統進行登出操作后,其他子系統會自動登出。
實際CAS登出的步驟為
所以每個子系統都需要實現一個sso登出響應。
cas-client-core包中有Single Sign Out的Session容器實現。
具體在包 org.jasig.cas.client.session 中。
2 實現Shiro的SSO登出功能
1 實現CasSecurityManager
主要目的是為了在登陸成功后保存 ST票據,並與 Shiro的sessionId進行關系映射。
1 /** 2 * 安全管理中心。<br> 3 * 主要目的是保存session和ticket之間的關系。 4 * @author Weicl 5 * @since 2016.4.25 6 */ 7 public class CasSecurityManager extends DefaultWebSecurityManager{ 8 9 Logger logger = LoggerFactory.getLogger(getClass()); 10 11 @Override 12 protected void onSuccessfulLogin(AuthenticationToken token, 13 AuthenticationInfo info, Subject subject) { 14 15 if (token instanceof CasToken) { 16 logger.info("save token info: " + token.getCredentials() + " -> " + subject.getSession(false).getId()); 17 SsoUtils.putTokenCache((String)token.getCredentials(), subject.getSession(false).getId()); 18 subject.getSession(false).setAttribute("_serviceTicket_", token.getCredentials()); 19 } 20 21 super.onSuccessfulLogin(token, info, subject); 22 } 23 }
PS: SsoUtils的putTokenCache。可以用ehcache或HashMap實現,其實沒關系。
2 實現LogoutSloFilter
這個攔截器用來處理sso登出請求。
1 /** 2 * 單點登出處理 3 * @author Weicl 4 * @since 2016.4.25 5 */ 6 public class LogoutSloFilter extends AdviceFilter{ 7 private final Logger logger = LoggerFactory.getLogger(getClass()); 8 private final Pattern pattern = Pattern.compile("<samlp:SessionIndex>([^<]*)</samlp:SessionIndex>"); 9 10 @Autowired 11 private NativeSessionManager nativeSessionManager; 12 13 @Override 14 protected boolean preHandle(ServletRequest request, ServletResponse response) 15 throws Exception { 16 17 18 try { 19 String logoutRequest = request.getParameter("logoutRequest"); 20 String serviceTicket = extractServiceTicket(logoutRequest); 21 22 logger.info(" slo serviceTicket : " + serviceTicket); 23 24 String sessionId = (String)SsoUtils.getTokenCache(serviceTicket); 25 nativeSessionManager.stop(new DefaultSessionKey(sessionId)); 26 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } 30 31 response.getWriter().write("OK"); 32 return false; 33 } 34 35 /** 36 * 獲取登出請求中的Ticket 37 * @param logoutRequest 38 * @return 39 */ 40 private String extractServiceTicket(String logoutRequest) { 41 Matcher matcher = pattern.matcher(logoutRequest); 42 if (matcher.find()) { 43 return matcher.group(1); 44 } 45 return ""; 46 } 47 }
3 實現TicketSessionListener
這個類的作用是當session終止的時候,釋放 SsoUtil 中的tokenCache。因為應用的session不存在了,保存這個映射關系也沒有意義,而且浪費緩存空間。
1 /** 2 * 票據及session監聽器 3 * @author Weicl 4 * @since 2016.4.25 5 */ 6 public class TicketSessionListener implements SessionListener{ 7 8 Logger logger = LoggerFactory.getLogger(getClass()); 9 10 @Override 11 public void onStart(Session session) { 12 13 } 14 15 @Override 16 public void onStop(Session session) { 17 logger.info("==============================="); 18 logger.info("stop session:" + session.getId()); 19 20 String ticket = (String)session.getAttribute("_serviceTicket_"); 21 if (ticket != null) { 22 logger.info("remove serviceTicket: " + ticket); 23 SsoUtils.removeTokenCache(ticket); 24 } 25 } 26 27 @Override 28 public void onExpiration(Session session) { 29 onStop(session); 30 } 31 }
4 配置到spring中
一下為添加/修正的關鍵代碼。其他shiro的配置這邊就不貼出來了。
1 <!-- Shiro權限過濾過濾器定義 --> 2 <bean name="shiroFilterChainDefinitions" class="java.lang.String"> 3 <constructor-arg> 4 <value> 5 /static/** = anon 6 /cas = cas 7 /login = authc 8 /logout = logout 9 /logoutSlo = logoutSlo 10 /** = user 11 </value> 12 </constructor-arg> 13 </bean> 14 15 <!-- 安全認證過濾器 --> 16 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 17 <property name="securityManager" ref="securityManager" /> 18 <property name="loginUrl" value="${cas.server.url}/login?service=${cas.project.url}${adminPath}/cas" /> 19 <!-- 20 <property name="loginUrl" value="${adminPath}/login" /> --> 21 <property name="successUrl" value="${adminPath}/login" /> 22 <property name="filters"> 23 <map> 24 <entry key="cas" value-ref="casFilter"/> 25 <entry key="authc" value-ref="formAuthenticationFilter"/> 26 <entry key="logout" value-ref="logoutFilter"></entry> 27 <entry key="logoutSlo" value-ref="logoutSloFilter"></entry> 28 </map> 29 </property> 30 <property name="filterChainDefinitions"> 31 <ref bean="shiroFilterChainDefinitions"/> 32 </property> 33 </bean> 34 35 <bean id="logoutSloFilter" class="cn.xxxxxx.base.modules.sys.security.LogoutSloFilter"> 36 </bean> 37 38 <!-- 定義Shiro安全管理配置 --> 39 <bean id="securityManager" class="cn.xxxxxx.base.common.security.shiro.session.CasSecurityManager"> 40 <!-- <property name="realm" ref="systemAuthorizingRealm" /> --> 41 <property name="realm" ref="systemCasRealm" /> 42 <property name="sessionManager" ref="sessionManager" /> 43 <property name="cacheManager" ref="shiroCacheManager" /> 44 </bean>