跳到主要内容
约 4 分钟3 张卡

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 过滤器的位置​

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 入口和内部调用。资源归属仍由业务授权约束。

12. 参考资料​