1. 程式人生 > >CAS和Spring-shiro結合實現單點登出功能

CAS和Spring-shiro結合實現單點登出功能

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了。