对于最新的稳定版本,请使用 Spring Security 6.4.1! |
会话管理
强制创建 Eager 会话
有时,急切地创建会话可能很有价值。
这可以通过使用 ForceEagerSessionCreationFilter
来完成,该过滤器可以使用以下方法进行配置:
-
Java
-
XML
@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-management
-
Java
-
XML
@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,例如,通过在注销处理程序中使用以下语法:
-
Java
-
XML
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.logout(logout -> logout
.deleteCookies("JSESSIONID")
);
return http.build();
}
<http>
<logout delete-cookies="JSESSIONID" />
</http>
不幸的是,这不能保证每个 servlet 容器都有效,因此您需要在您的环境中对其进行测试
如果您在代理后面运行应用程序,则还可以通过配置代理服务器来删除会话 Cookie。
例如,使用 Apache HTTPD 的 mod_headers,以下指令将通过在对注销请求的响应中使 cookie 过期来删除 cookie(假设应用程序部署在 path 下):
|
并发会话控制
如果您希望对单个用户登录应用程序的能力施加限制,Spring Security 通过以下简单的添加来支持开箱即用。 首先,您需要将以下侦听器添加到您的配置中,以使 Spring Security 了解会话生命周期事件:
-
Java
-
XML
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
然后将以下行添加到您的应用程序上下文中:
-
Java
-
XML
@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>
这将防止用户多次登录 - 第二次登录将导致第一次登录无效。 通常,您更愿意阻止第二次登录,在这种情况下,您可以使用
-
Java
-
XML
@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-url
session-authentication-error-url
session-management
如果您正在为基于表单的登录使用自定义身份验证过滤器,则必须显式配置并发会话控制支持。 更多详细信息可以在 Session Management 章节中找到。
会话固定攻击防护
会话固定攻击是一种潜在风险,恶意攻击者可能通过访问站点来创建会话,然后说服其他用户使用相同的会话登录(例如,通过向他们发送包含会话标识符作为参数的链接)。
Spring Security 通过在用户登录时创建新会话或更改会话 ID 来自动防止这种情况。
如果您不需要此保护,或者它与某些其他要求冲突,则可以使用属性 on 控制行为,该属性有四个选项session-fixation-protection
<session-management>
-
none
- 什么都不要做。 将保留原始会话。 -
newSession
- 创建一个新的 “clean” session,而不复制现有的 session 数据(与 Spring Security 相关的属性仍然会被复制)。 -
migrateSession
- 创建新会话并将所有现有会话属性复制到新会话。 这是 Servlet 3.0 或更早版本中的默认设置。 -
changeSessionId
- 不要创建新会话。 请改用 Servlet 容器 () 提供的会话固定保护。 此选项仅在 Servlet 3.1 (Java EE 7) 和更新的容器中可用。 在较旧的容器中指定它将导致异常。 这是 Servlet 3.1 和更高版本中的默认设置。HttpServletRequest#changeSessionId()
当发生会话固定保护时,它会导致 在应用程序上下文中发布。
如果使用 ,则此保护还会导致通知任何 s,因此,如果代码侦听这两个事件,请谨慎使用。
有关更多信息,请参阅 Session Management 章节。SessionFixationProtectionEvent
changeSessionId
javax.servlet.http.HttpSessionIdListener
会话管理过滤器
该 根据 的当前内容检查 的内容,以确定用户是否在当前请求期间已经过身份验证,通常是通过非交互式身份验证机制,例如 pre-authentication 或 remember-meSessionManagementFilter
SecurityContextRepository
SecurityContextHolder
[1].
如果存储库包含安全上下文,则筛选器不执行任何操作。
如果不是,并且 thread-local 包含一个 (非匿名) 对象,则 filter 会假定它们已经通过堆栈中前一个 filter 的身份验证。
然后,它将调用配置的 .SecurityContext
Authentication
SessionAuthenticationStrategy
如果用户当前未进行身份验证,则过滤器将检查是否请求了无效的会话 ID(例如,由于超时),并将调用配置的 ,如果已设置。
最常见的行为只是重定向到固定的 URL,这封装在 标准实施 中。
如前所述,当通过命名空间配置无效的会话 URL 时,也会使用后者。InvalidSessionStrategy
SimpleRedirectInvalidSessionStrategy
SessionAuthenticationStrategy
SessionAuthenticationStrategy
被 和 一起使用,因此,例如,如果您使用的是自定义的 form-login 类,则需要将其注入到这两个类中。
在这种情况下,将名称空间和自定义 bean 组合在一起的典型配置可能如下所示:SessionManagementFilter
AbstractAuthenticationProcessingFilter
<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。SessionFixationProtectionStrategy
HttpSessionBindingListener
并发控制
Spring Security 能够防止主体同时对同一应用程序进行身份验证超过指定次数。 许多 ISV 利用此功能来强制实施许可,而网络管理员喜欢此功能,因为它有助于防止人们共享登录名。 例如,您可以阻止用户 “Batman” 从两个不同的会话登录到 Web 应用程序。 您可以使他们之前的登录过期,也可以在他们尝试再次登录时报告错误,从而阻止第二次登录。 请注意,如果您使用的是第二种方法,则尚未明确注销的用户(例如,刚刚关闭了浏览器的用户)将无法再次登录,直到其原始会话过期。
命名空间支持并发控制,因此请查看前面的命名空间章节以获取最简单的配置。 不过,有时您需要自定义内容。
该实现使用名为 的专用版本的 。SessionAuthenticationStrategy
ConcurrentSessionControlAuthenticationStrategy
以前,并发身份验证检查由 进行,该检查可以通过 .
后者将检查用户是否尝试超过允许的会话数。
但是,这种方法需要提前创建 HTTP 会话,这是不可取的。
在 Spring Security 3 中,用户首先由 进行身份验证,一旦他们成功通过身份验证,就会创建一个会话,并检查是否允许他们打开另一个会话。 |
要使用并发会话支持,您需要将以下内容添加到:web.xml
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
此外,您需要将 添加到您的 .
需要两个构造函数参数 ,通常指向 的实例 ,以及 ,用于定义会话过期时要应用的策略。
使用名称空间创建和其他默认 bean 的配置可能如下所示:ConcurrentSessionFilter
FilterChainProxy
ConcurrentSessionFilter
sessionRegistry
SessionRegistryImpl
sessionInformationExpiredStrategy
FilterChainProxy
<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.xml
ApplicationEvent
ApplicationContext
HttpSession
SessionRegistryImpl
在 SessionRegistry 中查询当前已验证的用户及其会话
通过名称空间或使用普通 bean 设置 concurrency-control 具有有用的副作用,即为您提供可以直接在应用程序中使用的引用,因此即使您不想限制用户可能拥有的会话数,无论如何都值得设置基础结构。
您可以将该属性设置为 -1 以允许无限会话。
如果你正在使用命名空间,你可以使用属性为内部创建的设置一个别名,提供一个可以注入到你自己的 bean 中的引用。SessionRegistry
maximumSession
SessionRegistry
session-registry-alias
该方法为您提供当前经过身份验证的用户的列表。
您可以通过调用 该方法列出用户的会话,该方法将返回对象列表。
您还可以通过调用实例来使用户的会话过期。
当用户返回应用程序时,将阻止他们继续。
例如,您可能会发现这些方法在管理应用程序中很有用。
有关更多信息,请查看 Javadoc。getAllPrincipals()
getAllSessions(Object principal, boolean includeExpiredSessions)
SessionInformation
expireNow()
SessionInformation
SessionManagementFilter