你的位置:首页 > Java教程

[Java教程]在shiro


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   @Override12   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   @Autowired11   private NativeSessionManager nativeSessionManager;12   13   @Override14   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    * 获取登出请求中的Ticket37    * @param logoutRequest38    * @return39   */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   @Override11   public void onStart(Session session) {12     13   }14 15   @Override16   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   @Override28   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 = logoutSlo10         /** = user11       </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>