此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

用户首次请求受保护的资源时,系统会提示他们输入凭据。 提示输入凭据的最常见方法之一是将用户重定向到登录页面。 请求受保护资源的未经身份验证的用户的汇总 HTTP 交换可能如下所示:Spring中文文档

例 1.未经身份验证的用户请求受保护的资源
GET / HTTP/1.1
Host: example.com
Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b
HTTP/1.1 302 Found
Location: /login

用户提交其用户名和密码。Spring中文文档

用户名和密码已提交
POST /login HTTP/1.1
Host: example.com
Cookie: SESSION=91470ce0-3f3c-455b-b7ad-079b02290f7b

username=user&password=password&_csrf=35942e65-a172-4cd4-a1d4-d16a51147b3e

对用户进行身份验证后,用户将关联到新的会话 ID,以防止会话固定攻击Spring中文文档

经过身份验证的用户已关联到新会话
HTTP/1.1 302 Found
Location: /
Set-Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8; Path=/; HttpOnly; SameSite=Lax

后续请求包括会话 cookie,该 cookie 用于在会话的剩余时间内对用户进行身份验证。Spring中文文档

作为凭据提供的经过身份验证的会话
GET / HTTP/1.1
Host: example.com
Cookie: SESSION=4c66e474-3f5a-43ed-8e48-cc1d8cb1d1c8

SecurityContext存储库

在 Spring Security 中,用户与未来请求的关联是使用 SecurityContextRepository 进行的。 的默认实现是 DelegatingSecurityContextRepository,它委托给以下内容:SecurityContextRepositorySpring中文文档

HttpSessionSecurityContextRepository

HttpSessionSecurityContextRepositorySecurityContext 关联到 . 如果用户希望以另一种方式将用户与后续请求关联,或者根本不将用户关联起来,则可以将其替换为另一种实现。HttpSessionHttpSessionSecurityContextRepositorySecurityContextRepositorySpring中文文档

NullSecurityContextRepository

如果不希望将 关联到 (即在使用 OAuth 进行身份验证时),则 NullSecurityContextRepository 是其实现,它不执行任何操作。SecurityContextHttpSessionSecurityContextRepositorySpring中文文档

RequestAttributeSecurityContextRepository

RequestAttributeSecurityContextRepository 将 另存为请求属性,以确保该属性可用于跨调度类型发生的单个请求,这些请求可能会清除 .SecurityContextSecurityContextSecurityContextSpring中文文档

例如,假设客户端发出请求,经过身份验证,然后发生错误。 根据 servlet 容器实现,该错误意味着已建立的任何内容都将被清除,然后进行错误调度。 进行错误调度时,没有建立。 这意味着错误页面不能使用 for 授权或显示当前用户,除非以某种方式保留。SecurityContextSecurityContextSecurityContextSecurityContextSpring中文文档

使用 RequestAttributeSecurityContextRepository
public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.securityContext((securityContext) -> securityContext
			.securityContextRepository(new RequestAttributeSecurityContextRepository())
		);
	return http.build();
}
<http security-context-repository-ref="contextRepository">
	<!-- ... -->
</http>
<b:bean name="contextRepository"
	class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />

DelegatingSecurityContextRepository

DelegatingSecurityContextRepository 将 保存给多个委托,并允许按指定顺序从任何委托中检索。SecurityContextSecurityContextRepositorySpring中文文档

最有用的安排是使用以下示例进行配置,该示例允许同时使用 RequestAttributeSecurityContextRepositoryHttpSessionSecurityContextRepositorySpring中文文档

配置 DelegatingSecurityContextRepository
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	http
		// ...
		.securityContext((securityContext) -> securityContext
			.securityContextRepository(new DelegatingSecurityContextRepository(
				new RequestAttributeSecurityContextRepository(),
				new HttpSessionSecurityContextRepository()
			))
		);
	return http.build();
}
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
	http {
		// ...
		securityContext {
			securityContextRepository = DelegatingSecurityContextRepository(
				RequestAttributeSecurityContextRepository(),
				HttpSessionSecurityContextRepository()
			)
		}
	}
	return http.build()
}
<http security-context-repository-ref="contextRepository">
	<!-- ... -->
</http>
<bean name="contextRepository"
	class="org.springframework.security.web.context.DelegatingSecurityContextRepository">
		<constructor-arg>
			<bean class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
		</constructor-arg>
		<constructor-arg>
			<bean class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
		</constructor-arg>
</bean>

在 Spring Security 6 中,上面显示的示例是默认配置。Spring中文文档

在 Spring Security 6 中,上面显示的示例是默认配置。Spring中文文档

SecurityContextPersistenceFilter

SecurityContextPersistenceFilter 负责使用 SecurityContextRepository 在请求之间持久化。SecurityContextSpring中文文档

SecurityContextPersistence过滤器

数字 1在运行应用程序的其余部分之前,从 加载 并将其设置在 .SecurityContextPersistenceFilterSecurityContextSecurityContextRepositorySecurityContextHolderSpring中文文档

数字 2接下来,运行应用程序。Spring中文文档

数字 3最后,如果 已更改,我们使用 . 这意味着在使用 时,只需设置 将确保使用 持久化。SecurityContextSecurityContextSecurityContextPersistenceRepositorySecurityContextPersistenceFilterSecurityContextHolderSecurityContextSecurityContextRepositorySpring中文文档

在某些情况下,在方法完成之前,会提交响应并将其写入客户端。 例如,如果将重定向发送到客户端,则响应会立即写回客户端。 这意味着在步骤 3 中无法建立 an,因为会话 ID 无法包含在已编写的响应中。 可能发生的另一种情况是,如果客户端身份验证成功,则响应在完成之前提交,并且客户端在完成之前发出第二个请求,第二个请求中可能存在错误的身份验证。SecurityContextPersistenceFilterHttpSessionSecurityContextPersistenceFilterSecurityContextPersistenceFilterSpring中文文档

为了避免这些问题,包装 和 以检测是否已更改,如果是,则在提交响应之前保存。SecurityContextPersistenceFilterHttpServletRequestHttpServletResponseSecurityContextSecurityContextSpring中文文档

SecurityContextHolder筛选器

SecurityContextHolderFilter 负责使用 SecurityContextRepository 加载请求之间的请求。SecurityContextSpring中文文档

securitycontextholderfilter

数字 1在运行应用程序的其余部分之前,从 加载 并将其设置在 .SecurityContextHolderFilterSecurityContextSecurityContextRepositorySecurityContextHolderSpring中文文档

数字 2接下来,运行应用程序。Spring中文文档

SecurityContextPersistenceFilter 不同,它只加载 它不保存 . 这意味着在使用 时,需要显式保存 。SecurityContextHolderFilterSecurityContextSecurityContextSecurityContextHolderFilterSecurityContextSpring中文文档

显式保存 SecurityContext
public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.securityContext((securityContext) -> securityContext
			.requireExplicitSave(true)
		);
	return http.build();
}
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
    http {
        securityContext {
            requireExplicitSave = true
        }
    }
    return http.build()
}
<http security-context-explicit-save="true">
	<!-- ... -->
</http>

使用配置时,重要的是,任何设置 with a 的代码都会将 保存到 if 它应该在请求之间持久化。SecurityContextHolderSecurityContextSecurityContextSecurityContextRepositorySpring中文文档

例如,以下代码:Spring中文文档

设置方式SecurityContextHolderSecurityContextPersistenceFilter
SecurityContextHolder.setContext(securityContext);
SecurityContextHolder.setContext(securityContext)

应替换为Spring中文文档

设置方式SecurityContextHolderSecurityContextHolderFilter
SecurityContextHolder.setContext(securityContext);
securityContextRepository.saveContext(securityContext, httpServletRequest, httpServletResponse);
SecurityContextHolder.setContext(securityContext)
securityContextRepository.saveContext(securityContext, httpServletRequest, httpServletResponse)