Spring Security
Spring Security 在 Servlet 过滤器链中处理认证与授权。本文以 Spring Security 6.5 为基准。重点是多链匹配、上下文生命周期和方法权限。
1. 面试核心
先记住这几条,再读正文- 容器与 Spring 的衔接。
DelegatingFilterProxy将容器调用委托给 Spring 中的FilterChainProxy。 - 过滤器链选择。
FilterChainProxy每次选择第一条匹配的SecurityFilterChain。卡 · DelegatingFilterProxy - 安全上下文保存。 Spring Security 6 默认要求显式保存跨请求的安全上下文。卡 · 上下文持久化
- 安全异常处理。
ExceptionTranslationFilter将下游安全异常交给认证入口或拒绝处理器。 - 方法授权。
@EnableMethodSecurity启用方法授权,代理边界影响注解生效。卡 · 方法与数据级授权
2. DelegatingFilterProxy
Servlet 容器通过 DelegatingFilterProxy 进入安全链。它查找名为 springSecurityFilterChain 的 Spring Bean。该 Bean 的类型为 FilterChainProxy。
安全过滤器由 Spring 管理。重复注册到容器与安全链会造成重复执行。
3. FilterChainProxy
应用可以配置多条 SecurityFilterChain。@Order 控制检查顺序,securityMatcher 控制链的适用范围。
@Bean
@Order(1)
SecurityFilterChain api(HttpSecurity http) throws Exception {
http.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
这是 Spring Security 6.5 的配置片段。它只保护 /api/**。其他路径还需另一条兜底链。
requestMatchers 则选择链内的授权规则。请求未命中任何链时,不受 Spring Security 保护。测试应覆盖管理端点、HTTP method 和错误转发。
4. SecurityContext
SecurityContextHolder 持有当前执行线程的安全上下文。Authentication 包含主体、认证状态与 authorities。
Servlet 默认策略使用 ThreadLocal。请求结束后,安全链清理上下文。线程池复用才不会携带上次请求的身份。
异步任务可使用 DelegatingSecurityContextExecutor 等包装器。它在执行任务时设置上下文,结束时清理。业务还需控制向异步任务传递的身份范围。
4.1 上下文持久化
Spring Security 5 默认自动保存 SecurityContext。Spring Security 6 默认使用 SecurityContextHolderFilter。它负责读取上下文,持久化由认证流程显式完成。
自定义认证流程若需要跨请求登录,应调用 saveContext。目标是配置的 SecurityContextRepository。仅设置 SecurityContextHolder 只影响当前执行上下文。
内置认证机制会按自身配置处理保存。排查自定义登录时,需核对仓库与保存时机。
5. AuthenticationManager
认证过滤器提取凭据,构造待认证的 Authentication。AuthenticationManager 接收认证请求。ProviderManager 将请求交给支持该类型的 Provider。
Provider 验证成功后返回认证结果。过滤器将结果写入上下文。失败处理器负责输出响应,凭据值应避开日志。
6. 过滤器顺序
上下文读取在前,认证与授权随后进行。需要用户身份的自定义过滤器应位于认证之后。授权过滤器依据当前身份与规则作决定。
ExceptionTranslationFilter 只捕获其下游抛出的安全异常。位于它之前的认证过滤器使用自己的失败处理器。调试时应查看实际链顺序,并检查重复注册。
7. ExceptionTranslationFilter
认证入口由 AuthenticationEntryPoint 决定。表单登录可以重定向到登录页。Basic 或 Bearer API 可返回 401。
已认证主体权限不足时,默认拒绝处理返回 403。匿名主体的授权失败会启动认证入口。自定义处理器可改变响应形式。
因此,过滤器职责是转换异常并委托处理。它并非把所有认证异常固定转换为 401。
8. Session 与 Bearer Token
Session 登录通过上下文仓库跨请求恢复身份。无状态 API 每次验证 Bearer Token。
SessionCreationPolicy.STATELESS 控制安全上下文的 Session 使用。若凭据放在自动携带的 Cookie 中,仍需 CSRF 防护。具体机制见 XSS 与 CSRF。
9. 方法与数据级授权
authorizeHttpRequests 约束请求路径。@EnableMethodSecurity 启用方法权限。随后可用 @PreAuthorize 表达调用前检查。
默认代理模式下,同类内部调用绕过代理。因此,内部直接调用带注解的方法会漏掉方法拦截。入口应经过受保护的 Bean 代理。
方法权限还需结合资源归属与租户条件。数据层边界见 多租户边界。
10. 常见问题
10.1 多链匹配范围
每次选择第一条匹配的链。通用链排在前面,会遮蔽专用链。配置末尾可用覆盖所有请求的链兜底。
10.2 自定义 JWT 过滤器的位置
创建身份的过滤器应位于授权之前。认证失败需进入正确的失败处理流程。Resource Server 已提供 Bearer 支持,可直接配置使用。
11 · 面试题卡片
11.1 Spring Security 如何从请求进入过滤器链并完成鉴权?
出现公司:美团
考察重点:容器委托、多链匹配与上下文生命周期。
相关内容:DelegatingFilterProxy、过滤器顺序。
参考回答
容器通过 DelegatingFilterProxy 调用 FilterChainProxy。
后者选择首条匹配链,再按顺序调用过滤器。
认证过滤器委托 AuthenticationManager 验证凭据。认证结果进入 SecurityContext,授权过滤器再判断权限。请求结束后清理上下文。异常响应由相应 entry point 或失败处理器决定。
11.2 Spring Security 6 自定义登录后,下次请求为何仍匿名?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:显式保存与上下文仓库。
相关内容:上下文持久化。
参考回答
若只设置 ThreadLocal,上下文就仅在当前请求中生效。
Spring Security 6 默认要求显式保存跨请求状态。
排查时检查认证成功后是否调用 saveContext。再核对仓库配置、Session Cookie 与无状态策略。该问题也需结合实际登录实现定位。
11.3 同类调用带 PreAuthorize 的方法会触发权限检查吗?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:代理拦截、启用配置与调用边界。
相关内容:方法与数据级授权。
参考回答
默认代理模式下,同类内部调用绕过方法拦截。
@EnableMethodSecurity 启用后,外部调用还需经过代理。
可将受保护操作放在独立 Bean,并经代理调用。测试同时覆盖 HTTP 入口和内部调用。资源归属仍由业务授权约束。