Appearance
Spring Security
很多人第一次接触 Spring Security,最直接的感受通常是:“怎么一接进来,登录、权限、过滤器一下子全复杂了。”
这其实很正常,因为 Spring Security 关注的本来就不是单个接口怎么写,而是一个 Spring 应用如何系统化地做认证、授权和请求保护。
这篇文章重点讲:
Spring Security到底是什么- 它解决什么问题
- 认证和授权分别是什么意思
- 过滤器链为什么是它的核心
- 它和 Session、JWT、OAuth2 这些词分别是什么关系
1. Spring Security 到底是什么
Spring Security 可以看成 Spring 生态里的安全框架。
它主要负责的是:
- 身份认证
- 权限控制
- 请求保护
- 安全上下文管理
所以它不是单纯的“登录组件”,而是围绕 Web 应用和接口安全形成的一整套安全处理框架。
2. 它主要解决什么问题
如果没有统一安全框架,应用里的安全逻辑很容易散在各处:
- 这里自己判断有没有登录
- 那里自己判断有没有角色
- 另一个地方又自己做 token 校验
- 异常返回风格也不统一
这会带来两个明显问题:
- 安全规则分散,容易漏
- 维护成本高,且行为不一致
Spring Security 解决的核心问题是:把认证、授权和安全拦截这几类通用逻辑统一收口。
3. 认证和授权到底有什么区别
这是安全主线里最基础的边界。
3.1 认证
认证 解决的是“你是谁”。
例如:
- 用户名密码校验
- token 校验
- 登录态确认
3.2 授权
授权 解决的是“你能做什么”。
例如:
- 是否能访问管理后台
- 是否能调用某个接口
- 是否有某个角色或权限标识
可以直接记成:
- 认证回答“你是谁”
- 授权回答“你能干什么”
4. 为什么过滤器链是 Spring Security 的核心
Spring Security 在 Web 场景里,最核心的处理方式不是直接在 Controller 里到处写安全判断,而是让请求在进入业务层之前先经过一条安全过滤器链。
它解决的是:安全逻辑不要分散写在每个接口方法里,而要统一在请求入口层处理。
这条链路通常会承担:
- 提取认证信息
- 校验登录态
- 构建用户上下文
- 执行权限判断
- 处理未登录或无权限异常
真要抓住 Spring Security 的核心,重点不在某个注解,而在请求进入业务前已经先过了一整套安全过滤器链。
如果你想把 Filter、过滤器链、Interceptor、DispatcherServlet 这些基础角色分清,再回来看 Spring Security,会更容易理解:
4.1 一个最小安全配置示例
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
}
}这个配置最核心的意思是:
/login可以匿名访问/admin/**需要管理员角色- 其他请求默认要先认证
4.2 Spring Security 中到底有哪些过滤器链和 Interceptor 链路
很多人学到这里时,最容易混淆的一点是:Spring Security 自己到底有哪些链,它和 Spring MVC 的 Interceptor 又是什么关系。
可以直接记住一个结论:
Spring Security的 Web 安全主线,核心是Filter链Spring MVC的HandlerInterceptor不属于 Spring Security 主干- 如果开启方法级安全,还会再有一层“方法安全拦截器链”
在一个典型 Spring Boot Web 项目里,请求并不是只过一条链,而通常会先后经过几层不同职责的链路。
4.3 先分清 3 条最常见的链
4.3.1 Servlet Filter 链
这是最外层的请求入口链。
它属于 Servlet 容器层,Spring Security 也是通过这一层接入 Web 请求的。
你可以把它理解成:所有进入 Web 应用的 HTTP 请求,都会先经过这层 Filter 体系。
4.3.2 Spring Security Filter 链
这是 Spring Security 真正的核心主线。
把主链路摊开看,请求通常会先经过:
DelegatingFilterProxyFilterChainProxy- 匹配到某一条
SecurityFilterChain - 再执行这条链里的多个安全过滤器
这里的 SecurityFilterChain 可以理解成 Spring Security 为某一类请求准备的一组安全过滤规则集合。
不同请求路径,理论上可以匹配到不同的安全过滤链,而不是所有请求永远只共用一条完全相同的链。
4.3.3 Spring MVC HandlerInterceptor 链
这条链属于 Spring MVC,不属于 Spring Security 的核心过滤器链。
它发生在请求已经进入 DispatcherServlet 之后。
所以它更适合做:
- 审计
- 埋点
- 和具体 Handler 强相关的上下文准备
真正的认证、授权主线,工程上通常还是优先放在 Spring Security 的过滤器链里处理。
4.4 一条请求通常怎么走
看一张总流程图:
mermaid
flowchart TD
A[HTTP 请求进入应用] --> B[Servlet Filter 链]
B --> C[DelegatingFilterProxy]
C --> D[FilterChainProxy]
D --> E[匹配到某条 SecurityFilterChain]
E --> F[执行多个 Spring Security Filter]
F --> G[DispatcherServlet]
G --> H[HandlerInterceptor preHandle]
H --> I[Controller]
I --> J[Service]
J --> K[方法级安全拦截器]
K --> L[返回结果]
L --> M[HandlerInterceptor afterCompletion]
M --> N[HTTP 响应返回]这张图最重要的意思是:
- 安全过滤器链在
DispatcherServlet之前 - MVC 的
HandlerInterceptor在DispatcherServlet之后 - 方法级安全如果开启,通常发生在方法调用这一层
4.5 Spring Security 过滤器链里常见会有哪些过滤器
不同版本、不同配置、不同认证模式下,实际参与的过滤器并不完全相同。
但从职责上看,比较常见的可以分成下面几类:
| 过滤器 / 组件 | 主要职责 |
|---|---|
SecurityContextHolderFilter | 准备当前请求的安全上下文 |
HeaderWriterFilter | 写入常见安全响应头 |
CorsFilter | 处理跨域 |
CsrfFilter | 处理 CSRF 防护 |
LogoutFilter | 处理退出登录 |
UsernamePasswordAuthenticationFilter | 处理表单登录认证 |
BasicAuthenticationFilter | 处理 HTTP Basic 认证 |
BearerTokenAuthenticationFilter | 处理 Bearer Token / OAuth2 资源服务器认证 |
自定义 OncePerRequestFilter | 例如 JWT 解析、用户上下文装配 |
AnonymousAuthenticationFilter | 匿名用户兜底 |
SessionManagementFilter | 处理 Session 相关策略 |
ExceptionTranslationFilter | 把认证/授权异常转换成统一响应 |
AuthorizationFilter | 执行访问授权判断 |
可以把它们压缩理解成一条职责链:准备上下文 -> 提取认证信息 -> 完成认证 -> 异常翻译 -> 授权判断
这里要特别注意:过滤器的名字可以记不全,但你要知道它们是在“请求进入 Controller 之前”就把安全问题先处理掉。
4.6 Spring Security 有自己的 Interceptor 链吗
这里把 Spring MVC 里的 HandlerInterceptor 和 Security 主链路分开:
它不是 Spring Security 的核心链。
Spring Security 在 Web 场景里主要依赖的是 Filter 链,而不是 MVC 的 Interceptor。
但如果你开启了方法级安全,例如:
@PreAuthorize@PostAuthorize@Secured@RolesAllowed
那 Spring Security 确实还会再引入一层“方法安全拦截器链”。
这一层更像 AOP 风格的调用拦截,而不是 Servlet 或 MVC 里的请求拦截。
常见组件可以知道这些名字:
AuthorizationManagerBeforeMethodInterceptorAuthorizationManagerAfterMethodInterceptor- 旧版本里常见
MethodSecurityInterceptor
它们解决的是:当代码真正调用某个 Controller 或 Service 方法时,是否允许当前用户执行这个方法。
4.7 该怎么理解这 3 层链路的分工
工程里把这 3 层分开看会更顺:
Servlet Filter / Security Filter:更适合处理请求入口层的认证、授权、异常响应、安全上下文HandlerInterceptor:更适合处理 MVC 调用链里的审计、埋点、Handler 相关增强Method Security Interceptor:更适合处理“方法能不能调用”的最终权限判断
所以一条更完整的安全链路通常可以压缩成:请求先进安全过滤器链,再进 MVC 拦截器链,最后在方法调用层再看是否还要做方法级权限判断。
5. SecurityContext 是什么
SecurityContext 可以理解成 当前请求对应的安全上下文。
它主要保存的是:
- 当前是谁
- 当前用户有哪些权限
- 当前认证是否已经完成
所以它解决的是 请求进入系统后,后续代码怎么统一拿到当前登录用户和权限信息。
例如:
java
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();6. 用户名密码登录在它这里是怎么理解的
用户名密码登录只是认证方式之一。
它的主线通常可以看成:
- 请求带着用户名密码进来
- 框架把认证请求交给认证管理器
- 再由具体认证逻辑校验用户名密码
- 校验成功后,生成认证结果并放入安全上下文
这里真正要记住的,不是每个类名,而是:Spring Security 把认证过程做成了标准化管线,而不是每个项目自己散着写登录逻辑。
例如一个最简单的用户查询逻辑通常会长这样:
java
@Service
public class UserSecurityService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return User.withUsername(username)
.password("{noop}123456")
.roles("USER")
.build();
}
}7. Session、JWT、OAuth2 和它是什么关系
这些词经常和 Spring Security 一起出现,但不是一回事。
7.1 Session
Session 更偏传统服务端会话模式。
它的重点是:登录成功后,服务端保存会话状态,后续请求通过会话标识识别用户。
7.2 JWT
JWT 是一种 token 表达方式。
它更常出现在前后端分离和接口认证场景里。
它的重点是:把认证信息放进令牌,由请求携带并校验。
7.3 OAuth2
OAuth2 更偏授权协议,不等于单纯 token。
它解决的是 第三方应用如何在用户授权前提下访问受保护资源。
所以更稳妥的理解是:
- Spring Security 是安全框架
Session、JWT是常见认证承载方式OAuth2是授权协议体系
7.4 一个 JWT 认证思路示例
如果项目走 JWT 路线,通常不会把登录态放在服务端 Session,而是:
- 登录成功后签发 token
- 后续请求在 Header 里携带 token
- 过滤器负责解析 token 并写入
SecurityContext
一个简化的过滤器示意:
java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken("tom", null, List.of());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}8. Spring Security 常见能力有哪些
比较常见的能力通常包括:
- 登录认证
- 方法级权限控制
- URL 级权限控制
- 密码加密
- Session 管理
- Token 认证接入
- 异常处理和未授权响应
如果你想继续把“应用安全”这条线补齐,比较自然的延伸阅读通常是:
这说明它的价值不是单点功能,而是 把应用安全的常见能力收口成统一框架模型。
8.1 常用注解和配置入口
| 注解 / 组件 | 作用 |
|---|---|
@EnableWebSecurity | 开启 Web 安全配置 |
@EnableMethodSecurity | 开启方法级安全控制 |
@PreAuthorize | 方法调用前做表达式鉴权 |
@PostAuthorize | 方法返回后做鉴权 |
@Secured | 基于角色的简单方法鉴权 |
@RolesAllowed | 基于角色的标准注解鉴权 |
例如方法级权限控制:
java
@RestController
@RequestMapping("/admin")
public class AdminController {
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/dashboard")
public String dashboard() {
return "ok";
}
}9. 为什么很多项目觉得它“重”
因为安全问题本身就不轻。
Spring Security 让人觉得重,通常不是因为它设计得乱,而是因为它同时覆盖了:
- 认证
- 授权
- 过滤器链
- 安全上下文
- 异常处理
- 多种认证模式适配
所以它的“重”,本质上是:它在认真处理一整套安全问题,而不是只做一个登录表单。
10. 工程上最常见的几个误区
10.1 误区一:Spring Security 就是登录框架
不是。
登录只是认证入口之一,它真正覆盖的是整条安全链路。
10.2 误区二:用了 JWT 就不需要 Spring Security
JWT 只是认证信息的一种载体,不替代安全框架本身。
10.3 误区三:权限控制只要 Controller 上加注解就够了
接口注解只是授权表达方式之一,真正的安全治理还包括过滤链、上下文、异常响应和认证流程。
10.4 误区四:安全问题只在系统对外开放时才重要
只要系统有用户、有角色、有接口边界,安全问题就已经存在。
11. 一句话总结
Spring Security 的核心价值,是把认证、授权和请求保护统一收口成 Spring 应用中的安全基础设施,而不是让每个接口各自去写零散的安全判断。