此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.1spring-doc.cn

会话管理

强制创建 Eager 会话

有时,急切地创建会话可能很有价值。 这可以通过使用 ForceEagerSessionCreationFilter 来完成,该过滤器可以使用以下方法进行配置:spring-doc.cn

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
        );
    return http.build();
}
<http create-session="ALWAYS">

</http>

检测超时

您可以将 Spring Security 配置为检测无效会话 ID 的提交,并将用户重定向到相应的 URL。 这是通过以下元素实现的:session-managementspring-doc.cn

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .invalidSessionUrl("/invalidSession.htm")
        );
    return http.build();
}
<http>
...
<session-management invalid-session-url="/invalidSession.htm" />
</http>

请注意,如果使用此机制来检测会话超时,则当用户注销并重新登录而不关闭浏览器时,它可能会错误地报告错误。 这是因为当您使会话失效时,会话 Cookie 不会被清除,即使用户已注销,也会重新提交会话 Cookie。 您可以在注销时显式删除 JSESSIONID Cookie,例如,通过在注销处理程序中使用以下语法:spring-doc.cn

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .logout(logout -> logout
            .deleteCookies("JSESSIONID")
        );
    return http.build();
}
<http>
<logout delete-cookies="JSESSIONID" />
</http>

不幸的是,这不能保证每个 servlet 容器都有效,因此您需要在您的环境中对其进行测试spring-doc.cn

如果您在代理后面运行应用程序,则还可以通过配置代理服务器来删除会话 Cookie。 例如,使用 Apache HTTPD 的 mod_headers,以下指令将通过在对注销请求的响应中使 cookie 过期来删除 cookie(假设应用程序部署在 path 下):JSESSIONID/tutorialspring-doc.cn

<LocationMatch "/tutorial/logout">
Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT"
</LocationMatch>

并发会话控制

如果您希望对单个用户登录应用程序的能力施加限制,Spring Security 通过以下简单的添加来支持开箱即用。 首先,您需要将以下侦听器添加到您的配置中,以使 Spring Security 了解会话生命周期事件:spring-doc.cn

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}
<listener>
<listener-class>
	org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>

然后将以下行添加到您的应用程序上下文中:spring-doc.cn

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .maximumSessions(1)
        );
    return http.build();
}
<http>
...
<session-management>
	<concurrency-control max-sessions="1" />
</session-management>
</http>

这将防止用户多次登录 - 第二次登录将导致第一次登录无效。 通常,您更愿意阻止第二次登录,在这种情况下,您可以使用spring-doc.cn

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .maximumSessions(1)
            .maxSessionsPreventsLogin(true)
        );
    return http.build();
}
<http>
<session-management>
	<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
</http>

然后,第二次登录将被拒绝。 “rejected” 是指如果使用基于表单的登录,则用户将被发送到 。 如果第二次身份验证通过另一个非交互式机制进行,例如“remember-me”,则会向客户端发送“未授权”(401) 错误。 如果您想使用错误页面,则可以将属性添加到元素中。authentication-failure-urlsession-authentication-error-urlsession-managementspring-doc.cn

如果您正在为基于表单的登录使用自定义身份验证过滤器,则必须显式配置并发会话控制支持。 更多详细信息可以在 Session Management 章节中找到。spring-doc.cn

会话固定攻击防护

会话固定攻击是一种潜在风险,恶意攻击者可能通过访问站点来创建会话,然后说服其他用户使用相同的会话登录(例如,通过向他们发送包含会话标识符作为参数的链接)。 Spring Security 通过在用户登录时创建新会话或更改会话 ID 来自动防止这种情况。 如果您不需要此保护,或者它与某些其他要求冲突,则可以使用属性 on 控制行为,该属性有四个选项session-fixation-protection<session-management>spring-doc.cn

  • none- 什么都不要做。 将保留原始会话。spring-doc.cn

  • newSession- 创建一个新的 “clean” session,而不复制现有的 session 数据(与 Spring Security 相关的属性仍然会被复制)。spring-doc.cn

  • migrateSession- 创建新会话并将所有现有会话属性复制到新会话。 这是 Servlet 3.0 或更早版本中的默认设置。spring-doc.cn

  • changeSessionId- 不要创建新会话。 请改用 Servlet 容器 () 提供的会话固定保护。 此选项仅在 Servlet 3.1 (Java EE 7) 和更新的容器中可用。 在较旧的容器中指定它将导致异常。 这是 Servlet 3.1 和更高版本中的默认设置。HttpServletRequest#changeSessionId()spring-doc.cn

当发生会话固定保护时,它会导致 在应用程序上下文中发布。 如果使用 ,则此保护会导致通知任何 s,因此,如果代码侦听这两个事件,请谨慎使用。 有关更多信息,请参阅 Session Management 章节。SessionFixationProtectionEventchangeSessionIdjavax.servlet.http.HttpSessionIdListenerspring-doc.cn

会话管理过滤器

该 根据 的当前内容检查 的内容,以确定用户是否在当前请求期间已经过身份验证,通常是通过非交互式身份验证机制,例如 pre-authentication 或 remember-meSessionManagementFilterSecurityContextRepositorySecurityContextHolder[1]. 如果存储库包含安全上下文,则筛选器不执行任何操作。 如果不是,并且 thread-local 包含一个 (非匿名) 对象,则 filter 会假定它们已经通过堆栈中前一个 filter 的身份验证。 然后,它将调用配置的 .SecurityContextAuthenticationSessionAuthenticationStrategyspring-doc.cn

如果用户当前未进行身份验证,则过滤器将检查是否请求了无效的会话 ID(例如,由于超时),并将调用配置的 ,如果已设置。 最常见的行为只是重定向到固定的 URL,这封装在 标准实施 中。 如前所述,当通过命名空间配置无效的会话 URL 时,也会使用后者。InvalidSessionStrategySimpleRedirectInvalidSessionStrategyspring-doc.cn

SessionAuthenticationStrategy

SessionAuthenticationStrategy被 和 一起使用,因此,例如,如果您使用的是自定义的 form-login 类,则需要将其注入到这两个类中。 在这种情况下,将名称空间和自定义 bean 组合在一起的典型配置可能如下所示:SessionManagementFilterAbstractAuthenticationProcessingFilterspring-doc.cn

<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:bean>

<beans:bean id="sas" class=
"org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />

请注意,如果您在实现 Spring 会话范围的 bean 的会话中存储 bean,则使用 default 可能会导致问题。 有关更多信息,请参阅此类的 Javadoc。SessionFixationProtectionStrategyHttpSessionBindingListenerspring-doc.cn

并发控制

Spring Security 能够防止主体同时对同一应用程序进行身份验证超过指定次数。 许多 ISV 利用此功能来强制实施许可,而网络管理员喜欢此功能,因为它有助于防止人们共享登录名。 例如,您可以阻止用户 “Batman” 从两个不同的会话登录到 Web 应用程序。 您可以使他们之前的登录过期,也可以在他们尝试再次登录时报告错误,从而阻止第二次登录。 请注意,如果您使用的是第二种方法,则尚未明确注销的用户(例如,刚刚关闭了浏览器的用户)将无法再次登录,直到其原始会话过期。spring-doc.cn

命名空间支持并发控制,因此请查看前面的命名空间章节以获取最简单的配置。 不过,有时您需要自定义内容。spring-doc.cn

该实现使用名为 的专用版本的 。SessionAuthenticationStrategyConcurrentSessionControlAuthenticationStrategyspring-doc.cn

以前,并发身份验证检查由 进行,该检查可以通过 . 后者将检查用户是否尝试超过允许的会话数。 但是,这种方法需要提前创建 HTTP 会话,这是不可取的。 在 Spring Security 3 中,用户首先由 进行身份验证,一旦他们成功通过身份验证,就会创建一个会话,并检查是否允许他们打开另一个会话。ProviderManagerConcurrentSessionControllerAuthenticationManagerspring-doc.cn

要使用并发会话支持,您需要将以下内容添加到:web.xmlspring-doc.cn

<listener>
	<listener-class>
	org.springframework.security.web.session.HttpSessionEventPublisher
	</listener-class>
</listener>

此外,您需要将 添加到您的 . 需要两个构造函数参数 ,通常指向 的实例 ,以及 ,用于定义会话过期时要应用的策略。 使用名称空间创建和其他默认 bean 的配置可能如下所示:ConcurrentSessionFilterFilterChainProxyConcurrentSessionFiltersessionRegistrySessionRegistryImplsessionInformationExpiredStrategyFilterChainProxyspring-doc.cn

<http>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />

<session-management session-authentication-strategy-ref="sas"/>
</http>

<beans:bean id="redirectSessionInformationExpiredStrategy"
class="org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/session-expired.htm" />
</beans:bean>

<beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:constructor-arg name="sessionInformationExpiredStrategy" ref="redirectSessionInformationExpiredStrategy" />
</beans:bean>

<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.CompositeSessionAuthenticationStrategy">
<beans:constructor-arg>
	<beans:list>
	<beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
		<beans:constructor-arg ref="sessionRegistry"/>
		<beans:property name="maximumSessions" value="1" />
		<beans:property name="exceptionIfMaximumExceeded" value="true" />
	</beans:bean>
	<beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
	</beans:bean>
	<beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
		<beans:constructor-arg ref="sessionRegistry"/>
	</beans:bean>
	</beans:list>
</beans:constructor-arg>
</beans:bean>

<beans:bean id="sessionRegistry"
	class="org.springframework.security.core.session.SessionRegistryImpl" />

将侦听器添加到 将导致每次 a 开始或结束时都会将 an 发布到 Spring。 这很关键,因为它允许在会话结束时收到通知。 如果没有它,一旦用户超过其会话限额,他们将永远无法再次登录,即使他们退出另一个会话或超时也是如此。web.xmlApplicationEventApplicationContextHttpSessionSessionRegistryImplspring-doc.cn

在 SessionRegistry 中查询当前已验证的用户及其会话

通过名称空间或使用普通 bean 设置 concurrency-control 具有有用的副作用,即为您提供可以直接在应用程序中使用的引用,因此即使您不想限制用户可能拥有的会话数,无论如何都值得设置基础结构。 您可以将该属性设置为 -1 以允许无限会话。 如果你正在使用命名空间,你可以使用属性为内部创建的设置一个别名,提供一个可以注入到你自己的 bean 中的引用。SessionRegistrymaximumSessionSessionRegistrysession-registry-aliasspring-doc.cn

该方法为您提供当前经过身份验证的用户的列表。 您可以通过调用 该方法列出用户的会话,该方法将返回对象列表。 您还可以通过调用实例来使用户的会话过期。 当用户返回应用程序时,将阻止他们继续。 例如,您可能会发现这些方法在管理应用程序中很有用。 有关更多信息,请查看 Javadoc。getAllPrincipals()getAllSessions(Object principal, boolean includeExpiredSessions)SessionInformationexpireNow()SessionInformationspring-doc.cn


1. 验证后执行重定向的机制(如 form-login)的验证不会被检测到,因为在验证请求期间不会调用过滤器。在这些情况下,必须单独处理会话管理功能。SessionManagementFilter