此节介绍:ConcurrentSessionFilter。
在Acegi 1.x版本中,控制并发HttpSession和Remember-Me认证服务不能够同时启用,它们之间存在冲突问题。
在一些应用场合,企业可能需要限制同一帐号在同一时间登录到同一Web应用的次数,即控制并发HttpSession的数量。比如,在同一时间,只允许javaee/password用户在服务器存在一个或若干个活动HttpSession。Acegi内置了HttpSession的并发控制支持,为我们提供了方便。为了启用这一企业特性,开发者必须完成如下几方面的配置。
1、将concurrentSessionFilter添加到filterChainProxy中
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=concurrentSessionFilter,httpSessionContextIntegrationFilter, logoutFilter,authenticationProcessingFilter, anonymousProcessingFilter,exceptionTranslationFilter, filterInvocationInterceptor </value> </property> </bean>
2、配置ConcurrentSessionFilter
控制并发HttpSession的过滤器,concurrentSessionFilter暴露了sessionRegistry和expiredUrl属性,sessionRegistry引用一个HttpSession注册器,这一注册器动态维护了登录到这一Web应用的所有HttpSession信息。
ConcurrentSessionFilter起到两个作用:
第一、对每个请求, 它都会调用org.acegisecurity.concurrent.SessionRegistry.refreshLastRequest(String)方法,这样保证所有已注册的会话总是有正确的最后修改时间。
第二、它会从SessionRegistry为每个请求返回org.acegisecurity.concurrent.SessionInformation会话信息,并且检查session是否已标记为过期。如果已标记为过期,那么该session将会被invalidate()掉。
<bean id="concurrentSessionFilter" class="org.acegisecurity.concurrent.ConcurrentSessionFilter"> <!-- 每次HttpSession开始或者结束的时候,web.xml中的HttpSessionEventPublisher都会发布一个 ApplicationEvent事件到Spring的ApplicationContext。 这是至关重要的,因为这个机制允许在session结束的时候,SessionRegistryImpl会得到通知。 这解释了为什么我们需要在ConcurrentSessionFilter中指向 SessionRegistryImpl的实例。--> <property name="sessionRegistry" ref="sessionRegistry"></property> <!-- 如果concurrentSessionController的exceptionIfMaximumExceeded属性设置为true,那么一旦并发HttpSession数量超过限额,将会重定向到expiredUrl指定的路径 --> <property name="expiredUrl"> <value>/concurrentError.jsp</value> </property> </bean> <!-- 注意,我们的程序一般并不用直接与SessionRegistryImpl打交道,你只需在Spring的配置文件定义一个Bean就行了 --> <bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl"> </bean>
3、配置HttpSessionEventPublisher
SessionRegistry的实现类是SessionRegistryImpl,SessionRegistryImpl实现了ApplicationListener,而且它会负责应对HttpSessionDestroyedEvent事件。一旦SessionRegistryImpl接收到HttpSessionDestroyedEvent事件,则它会把触发这一事件的HttpSession的相关信息从当前注册器中清除掉。因此,HttpSessionEventPublisher的配置工作显得格外重要。在web.xml中添加
<listener> <listener-class> org.acegisecurity.ui.session.HttpSessionEventPublisher </listener-class> </listener>
4、配置concurrentSessionController
并发HttpSession控制器, concurrentSessionController只需要一个sessionRegistry实例的属性,但它应用时还使用了maximumSessions属性,把它赋值为1。在ConcurrentSessionControllerImpl的源代码中,这个值初始值其实也是1。
它的用法和用处是如下:
(1)可以给它赋值-1,那么将不会限制并发登录。
(2)可以给它赋大于0值,这个数字限制相同用户名同时成功并发登录同一个web应用的次数。
注意:不可以给它赋值为0,这是不允许的。
例如,如果这个值是3,那么用户名为javaee的用户可以同时在3个地点同时成功登录进web应用,只要密码正确。
maximumSessions:允许用户创建的最大并发HttpSession数量。
exceptionIfMaximumExceeded:一旦并发HttpSession数量超过限额,是否抛出异常。
<bean id="concurrentSessionController" class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl"> <property name="maximumSessions" value="1"></property> <property name="sessionRegistry" ref="sessionRegistry"></property> <!-- 一般设置为false. 为true时, 如果已有一个该用户登录了, 那么在另一个地方登录该用户将抛出异常 如果设置为false, 那么, 如果已有一个该用户登录了系统, 那么在另一个地方也可以登录, 登录后前者会被逼退出系统 --> <property name="exceptionIfMaximumExceeded" value="true"></property> </bean> <!--一旦认证管理器成功认证了Authentication请求,ProviderManager会立即调用concurrentSessionController验证当前用户的已登录信息。--> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> …… <!-- 增加 --> <property name="sessionController" ref="concurrentSessionController"></property> </bean>