会话管理

HTTP会话相关的功能是由SessionManagementFilterSessionAuthenticationStrategy接口联合处理的, 过滤器会代理它们。 典型的应用包括会话伪造攻击预防,检测会话超时, 限制已登录用户可以同时打开多少会话。

11.1. SessionManagementFilter

SessionManagementFilter会检测 SecurityContextRepository的内容,比较当前 SecurityContextHolder,决定用户在当前请求中是否已经登录, 通常被一个非内部认证机制,比如预验证或remember-me[11] 如果资源库中包含一个安全上下文,过滤器什么也不会做。如果没有包含,thread-local中 SecurityContext包含一个(非匿名) Authentication对象,过滤器就会假设他们已经在 过滤器栈中的前一个过滤器中被认证过了。它会调用配置好的 SessionAuthenticationStrategy

如果用户当前没有认证,过滤器会检测是否有无效的session ID被请求了(比如,因为超时) 并会重定向到配置好的invalidSessionUrl,如果设置了。 最简单的配置方法是通过命名空间,如前面描述的

11.2. SessionAuthenticationStrategy

SessionAuthenticationStrategySessionManagementFilterAbstractAuthenticationProcessingFilter都是用了, 所以如果你使用了一个自定义的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>

      

11.3. 同步会话

Spring Security可以防止一个主体同时被一个相同的应用授权多于一个特定的次数。 许多ISV可以利用这一点来加强协议,网络管理员就很喜欢这点功能,因为它可以防止人们共享登陆账号。 你可以,比如,停止在两个不同的会话中的登陆web应用的用户“Batman”。 你可以选择让上一次登录过期,或者当他们尝试重复登录时报告一个错误,防止第二次登录。 注意,如果你使用第二种方式,一个用户如果没有使用注销(而是仅仅关闭了他们的浏览器,比如) 就不能再次登陆了,直到他们之前的会话失效之前。

这个功能在命名空间中已经支持了,所以请参考前面介绍命名空间的章节, 来了解简便的配置方式。有时你需要做些一次自定义的工作。

实现使用了特定版本的SessionAuthenticationStrategy, 称作ConcurrentSessionControlStrategy

Note

之前的验证检测是通过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检测, 因为过滤器不会在验证请求过程中被调用。 会话管理功能必须在这些情况下单独处理。