CAS既然有单点登录功能,那么自然有单点登出,意思就是其中一个子系统登出之后,其他所有系统都不能访问。
下面我要说的是CAS和Spring-shiro结合实现单点登出功能结合实现的单点登出功能
这是应用系统web.xml配置,这里要特别注意,登出校验的配置一定要写在Spring-shiro配置之前,否则会使单点登出不成功
<!-- 该过滤器用于实现单点登出功能,可选配置。 -->
<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>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://localhost:8081/cas/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Apache Shiro -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--CAS SSO-->
<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://localhost:8081/cas/login</param-value>
</init-param>
<context-param>
<param-name>renew</param-name>
<param-value>false</param-value>
</context-param>
<init-param>
<param-name>gateway</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8888</param-value>
</init-param>
<init-param>
<param-name>ignorePattern</param-name>
<param-value>/statistic/*|/static/*|/js/*|/img/*|/views/*|/css/*|webservice/*|/cas/changeCenter/*</param-value>
</init-param>
</filter>
<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://localhost:8081/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8888</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.HttpServletRequestWrapperFilter
</filter-class>
</filter>
<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 Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
下面是Spring-shiro的配置文件
<!-- Shiro权限过滤过滤器定义 -->
<bean name="shiroFilterChainDefinitions" class="java.lang.String">
<constructor-arg>
<value>
/img/** = anon
/static/** = anon
/userfiles/** = anon
${adminPath}/cas = cas
${adminPath}/cas/controlCenter/** = cas
${adminPath}/app/csWebappSso/** = user
${adminPath}/login = authc
${adminPath}/logout = logout
${adminPath}/** = user
/act/editor/** = user
/ReportServer/** = user
</value>
</constructor-arg>
</bean>
<bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl"
value="${cas.logout.url}"/>
</bean>
<!-- 安全认证过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- <property name="loginUrl" value="${cas.server.url}/login?service=${cas.project.url}${adminPath}/cas"/> -->
<property name="loginUrl" value="${adminPath}/login"/>
<property name="successUrl" value="${adminPath}?login" />
<property name="filters">
<map>
<entry key="cas" value-ref="casFilter" />
<entry key="authc" value-ref="formAuthenticationFilter" />
<entry key="logout" value-ref="logout"/>
</map>
</property>
<property name="filterChainDefinitions">
<ref bean="shiroFilterChainDefinitions" />
</property>
</bean>
<!-- cas和shiro结合 -->
<bean id="casRealm" class="com.wdim.modules.sys.security.CasLoginRealm">
<property name="casServerUrlPrefix" value="${cas.server.url}"></property>
<property name="casService" value="${cas.project.url}${adminPath}/cas"></property>
</bean>
<!-- CAS认证过滤器 -->
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<property name="failureUrl" value="${adminPath}/login" />
</bean>
<!-- 定义Shiro安全管理配置 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- <property name="realm" ref="systemAuthorizingRealm" /> -->
<property name="realms">
<list>
<ref bean="casRealm"/>
<ref bean="csSystemAuthorizingRealm"/>
</list>
</property>
<property name="sessionManager" ref="sessionManager" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 配置使用自定义认证器,可以实现多Realm认证,并且可以指定特定Realm处理特定类型的验证 -->
<bean id="authenticator" class="com.wdim.modules.sys.security.CustomizedModularRealmAuthenticator">
<!-- 配置认证策略,只要有一个Realm认证成功即可,并且返回所有认证成功信息 -->
<property name="authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
</property>
</bean>
<!-- 自定义会话管理配置 -->
<bean id="sessionManager"
class="com.wdim.common.security.shiro.session.SessionManager">
<property name="sessionDAO" ref="sessionDAO" />
<!-- 会话超时时间,单位:毫秒 -->
<property name="globalSessionTimeout" value="${session.sessionTimeout}" />
<!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话 -->
<property name="sessionValidationInterval" value="${session.sessionTimeoutClean}" />
<!-- <property name="sessionValidationSchedulerEnabled" value="false"/> -->
<property name="sessionValidationSchedulerEnabled" value="true" />
<property name="sessionIdCookie" ref="sessionIdCookie" />
<property name="sessionIdCookieEnabled" value="true" />
</bean>
<!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="jeesite.session.id" />
</bean>
<!-- 自定义Session存储容器 -->
<!-- <bean id="sessionDAO" class="com.wdimmmon.security.shiro.session.JedisSessionDAO"> -->
<!-- <property name="sessionIdGenerator" ref="idGen" /> -->
<!-- <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_"
/> -->
<!-- </bean> -->
<bean id="sessionDAO"
class="com.wdim.common.security.shiro.session.CacheSessionDAO">
<property name="sessionIdGenerator" ref="idGen" />
<property name="activeSessionsCacheName" value="activeSessionsCache" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 自定义系统缓存管理器 -->
<!-- <bean id="shiroCacheManager" class="com.wdimmmon.security.shiro.cache.JedisCacheManager"> -->
<!-- <property name="cacheKeyPrefix" value="${redis.keyPrefix}_cache_" /> -->
<!-- </bean> -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- AOP式方法级权限检查 -->
<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
注意了,这里shiro配置的是不使用servlet容器来管理session,而是自定义了shiro自己的会话管理,这样会导致意想不到的情况发生。再这样的配置下,cas server登出之后,并不能使子系统的session也注销掉,子系统还是可以正常的访问,单点登出功能并没有实现。要想实现单点登出功能,配置spring-shiro时不能自定义session管理器,必须使用servlet容器。那要怎样才能使用servlet容器呢?只要把我之前配置的注释掉就可以了,shiro在没有自定义回话管理器是会默认是用servlet的容器。
<!-- 自定义会话管理配置 -->
<bean id="sessionManager"
class="com.wdim.common.security.shiro.session.SessionManager">
<property name="sessionDAO" ref="sessionDAO" />
<!-- 会话超时时间,单位:毫秒 -->
<property name="globalSessionTimeout" value="${session.sessionTimeout}" />
<!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话 -->
<property name="sessionValidationInterval" value="${session.sessionTimeoutClean}" />
<!-- <property name="sessionValidationSchedulerEnabled" value="false"/> -->
<property name="sessionValidationSchedulerEnabled" value="true" />
<property name="sessionIdCookie" ref="sessionIdCookie" />
<property name="sessionIdCookieEnabled" value="true" />
</bean>
把这一个bean注释掉,然后把引用这个bean的地方也注释掉,就OK了。