HTTP会话相关的功能是由SessionManagementFilter
和SessionAuthenticationStrategy
接口联合处理的,
过滤器会代理它们。
典型的应用包括会话伪造攻击预防,检测会话超时,
限制已登录用户可以同时打开多少会话。
SessionManagementFilter
会检测
SecurityContextRepository
的内容,比较当前
SecurityContextHolder
,决定用户在当前请求中是否已经登录,
通常被一个非内部认证机制,比如预验证或remember-me[11]
如果资源库中包含一个安全上下文,过滤器什么也不会做。如果没有包含,thread-local中
SecurityContext
包含一个(非匿名)
Authentication
对象,过滤器就会假设他们已经在
过滤器栈中的前一个过滤器中被认证过了。它会调用配置好的
SessionAuthenticationStrategy
。
如果用户当前没有认证,过滤器会检测是否有无效的session ID被请求了(比如,因为超时)
并会重定向到配置好的invalidSessionUrl
,如果设置了。
最简单的配置方法是通过命名空间,如前面描述的。
SessionAuthenticationStrategy
被
SessionManagementFilter
和
AbstractAuthenticationProcessingFilter
都是用了,
所以如果你使用了一个自定义的formlogin类,比如,你需要把它注入到两者中。
比如,在典型配种,结合命名空间和自定义bean看起来像这样:
<http> <custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" /> <session-management session-authentication-strategy-ref="sas"/> </http> <beans:bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <beans:property name="sessionAuthenticationStrategy" ref="sas" /> <beans:property name="authenticationManager" ref="authenticationManager" /> </beans:bean> <beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"> <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" /> <beans:property name="maximumSessions" value="1" /> </beans:bean>
Spring Security可以防止一个主体同时被一个相同的应用授权多于一个特定的次数。 许多ISV可以利用这一点来加强协议,网络管理员就很喜欢这点功能,因为它可以防止人们共享登陆账号。 你可以,比如,停止在两个不同的会话中的登陆web应用的用户“Batman”。 你可以选择让上一次登录过期,或者当他们尝试重复登录时报告一个错误,防止第二次登录。 注意,如果你使用第二种方式,一个用户如果没有使用注销(而是仅仅关闭了他们的浏览器,比如) 就不能再次登陆了,直到他们之前的会话失效之前。
这个功能在命名空间中已经支持了,所以请参考前面介绍命名空间的章节, 来了解简便的配置方式。有时你需要做些一次自定义的工作。
实现使用了特定版本的SessionAuthenticationStrategy
,
称作ConcurrentSessionControlStrategy
。
之前的验证检测是通过ProviderManager
实现的,
可能是被注入了一个ConcurrentSessionController
,它可以检测
用户是否视图超过最大会话限制的允许数量。然而,这种方式要求预先创建一个HTTP会话,
这是不合理的。在Spring Security 3中,用户首先被AuthenticationManager
验证
一旦认证成功,就会创建一个会话,并进行检测,是否允许另一个会话打开。
如果要使用concurernt session支持,你需要向
web.xml
添加如下内容:
<listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener>
另外,你需要把
org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter
添加到你的FilterChainProxy
中。
ConcurrentSessionFilter
需要两个属性,
sessionRegistry
,通常是一个SessionRegistryImpl
的实例。
和expiredUrl
,这指向一个页面,当会话过期的时候就会显示它。
一个配置在命名空间中会创建FilterChainProxy
和其他bean
可能看起来就像这样:
<http> <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" /> <custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" /> <session-management session-authentication-strategy-ref="sas"/> </http> <beans:bean id="concurrencyFilter" class="org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter"> <beans:property name="sessionRegistry" ref="sessionRegistry" /> <beans:property name="expiredUrl" value="/session-expired.htm" /> </beans:bean> <beans:bean id="myAuthFilter" class="org.springframework.security.web.authentcation.UsernamePasswordAuthenticationFilter"> <beans:property name="sessionAuthenticationStrategy" ref="sas" /> ... </beans:bean> <beans:bean id="sas" class="org.springframework.security.web.session.ConcurrentSessionControlStrategy"> <beans:property name="sessionRegistry" ref="sessionRegistry" /> <beans:property name="maximumSessions" value="1" /> </beans:bean> <beans:bean id="sessionRegistry" class="org.springframework.security.authentication.concurrent.SessionRegistryImpl" />
在web.xml
添加的监听器,会触发一个ApplicationEvent
发布到Spring ApplicationContext
中,每当一个HttpSession
生成或销毁时。这是非常重要,它允许SessionRegistryImpl
在会话结束时被提醒。
没有它的话,用户就永远不能再登陆到系统中了,一旦他们超过了他们允许的绘画最大量,即使他们
注销了另外的会话,或会话超时了。
[11] 在那些会执行重定向的
认证机制中(比如form-based),无法使用SessionManagementFilter
检测,
因为过滤器不会在验证请求过程中被调用。
会话管理功能必须在这些情况下单独处理。