Skip to content

Spring Security

很多人第一次接触 Spring Security,最直接的感受通常是:“怎么一接进来,登录、权限、过滤器一下子全复杂了。”

这其实很正常,因为 Spring Security 关注的本来就不是单个接口怎么写,而是一个 Spring 应用如何系统化地做认证、授权和请求保护。

这篇文章重点讲:

  1. Spring Security 到底是什么
  2. 它解决什么问题
  3. 认证和授权分别是什么意思
  4. 过滤器链为什么是它的核心
  5. 它和 Session、JWT、OAuth2 这些词分别是什么关系

1. Spring Security 到底是什么

Spring Security 可以看成 Spring 生态里的安全框架。

它主要负责的是:

  1. 身份认证
  2. 权限控制
  3. 请求保护
  4. 安全上下文管理

所以它不是单纯的“登录组件”,而是围绕 Web 应用和接口安全形成的一整套安全处理框架。


2. 它主要解决什么问题

如果没有统一安全框架,应用里的安全逻辑很容易散在各处:

  1. 这里自己判断有没有登录
  2. 那里自己判断有没有角色
  3. 另一个地方又自己做 token 校验
  4. 异常返回风格也不统一

这会带来两个明显问题:

  1. 安全规则分散,容易漏
  2. 维护成本高,且行为不一致

Spring Security 解决的核心问题是:把认证、授权和安全拦截这几类通用逻辑统一收口。


3. 认证和授权到底有什么区别

这是安全主线里最基础的边界。

3.1 认证

认证 解决的是“你是谁”。

例如:

  1. 用户名密码校验
  2. token 校验
  3. 登录态确认

3.2 授权

授权 解决的是“你能做什么”。

例如:

  1. 是否能访问管理后台
  2. 是否能调用某个接口
  3. 是否有某个角色或权限标识

可以直接记成:

  1. 认证回答“你是谁”
  2. 授权回答“你能干什么”

4. 为什么过滤器链是 Spring Security 的核心

Spring Security 在 Web 场景里,最核心的处理方式不是直接在 Controller 里到处写安全判断,而是让请求在进入业务层之前先经过一条安全过滤器链。

它解决的是:安全逻辑不要分散写在每个接口方法里,而要统一在请求入口层处理。

这条链路通常会承担:

  1. 提取认证信息
  2. 校验登录态
  3. 构建用户上下文
  4. 执行权限判断
  5. 处理未登录或无权限异常

真要抓住 Spring Security 的核心,重点不在某个注解,而在请求进入业务前已经先过了一整套安全过滤器链。

如果你想把 Filter、过滤器链、InterceptorDispatcherServlet 这些基础角色分清,再回来看 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();
    }
}

这个配置最核心的意思是:

  1. /login 可以匿名访问
  2. /admin/** 需要管理员角色
  3. 其他请求默认要先认证

4.2 Spring Security 中到底有哪些过滤器链和 Interceptor 链路

很多人学到这里时,最容易混淆的一点是:Spring Security 自己到底有哪些链,它和 Spring MVC 的 Interceptor 又是什么关系。

可以直接记住一个结论:

  1. Spring Security 的 Web 安全主线,核心是 Filter
  2. Spring MVCHandlerInterceptor 不属于 Spring Security 主干
  3. 如果开启方法级安全,还会再有一层“方法安全拦截器链”

在一个典型 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 真正的核心主线。

把主链路摊开看,请求通常会先经过:

  1. DelegatingFilterProxy
  2. FilterChainProxy
  3. 匹配到某一条 SecurityFilterChain
  4. 再执行这条链里的多个安全过滤器

这里的 SecurityFilterChain 可以理解成 Spring Security 为某一类请求准备的一组安全过滤规则集合。

不同请求路径,理论上可以匹配到不同的安全过滤链,而不是所有请求永远只共用一条完全相同的链。

4.3.3 Spring MVC HandlerInterceptor

这条链属于 Spring MVC,不属于 Spring Security 的核心过滤器链。

它发生在请求已经进入 DispatcherServlet 之后。

所以它更适合做:

  1. 审计
  2. 埋点
  3. 和具体 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 响应返回]

这张图最重要的意思是:

  1. 安全过滤器链在 DispatcherServlet 之前
  2. MVC 的 HandlerInterceptorDispatcherServlet 之后
  3. 方法级安全如果开启,通常发生在方法调用这一层

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。

但如果你开启了方法级安全,例如:

  1. @PreAuthorize
  2. @PostAuthorize
  3. @Secured
  4. @RolesAllowed

那 Spring Security 确实还会再引入一层“方法安全拦截器链”。

这一层更像 AOP 风格的调用拦截,而不是 Servlet 或 MVC 里的请求拦截。

常见组件可以知道这些名字:

  1. AuthorizationManagerBeforeMethodInterceptor
  2. AuthorizationManagerAfterMethodInterceptor
  3. 旧版本里常见 MethodSecurityInterceptor

它们解决的是:当代码真正调用某个 Controller 或 Service 方法时,是否允许当前用户执行这个方法。

4.7 该怎么理解这 3 层链路的分工

工程里把这 3 层分开看会更顺:

  1. Servlet Filter / Security Filter:更适合处理请求入口层的认证、授权、异常响应、安全上下文
  2. HandlerInterceptor:更适合处理 MVC 调用链里的审计、埋点、Handler 相关增强
  3. Method Security Interceptor:更适合处理“方法能不能调用”的最终权限判断

所以一条更完整的安全链路通常可以压缩成:请求先进安全过滤器链,再进 MVC 拦截器链,最后在方法调用层再看是否还要做方法级权限判断。


5. SecurityContext 是什么

SecurityContext 可以理解成 当前请求对应的安全上下文

它主要保存的是:

  1. 当前是谁
  2. 当前用户有哪些权限
  3. 当前认证是否已经完成

所以它解决的是 请求进入系统后,后续代码怎么统一拿到当前登录用户和权限信息

例如:

java
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();

6. 用户名密码登录在它这里是怎么理解的

用户名密码登录只是认证方式之一。

它的主线通常可以看成:

  1. 请求带着用户名密码进来
  2. 框架把认证请求交给认证管理器
  3. 再由具体认证逻辑校验用户名密码
  4. 校验成功后,生成认证结果并放入安全上下文

这里真正要记住的,不是每个类名,而是: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. SessionJWTOAuth2 和它是什么关系

这些词经常和 Spring Security 一起出现,但不是一回事。

7.1 Session

Session 更偏传统服务端会话模式。

它的重点是:登录成功后,服务端保存会话状态,后续请求通过会话标识识别用户。

7.2 JWT

JWT 是一种 token 表达方式。

它更常出现在前后端分离和接口认证场景里。

它的重点是:把认证信息放进令牌,由请求携带并校验。

7.3 OAuth2

OAuth2 更偏授权协议,不等于单纯 token。

它解决的是 第三方应用如何在用户授权前提下访问受保护资源

所以更稳妥的理解是:

  1. Spring Security 是安全框架
  2. SessionJWT 是常见认证承载方式
  3. OAuth2 是授权协议体系

7.4 一个 JWT 认证思路示例

如果项目走 JWT 路线,通常不会把登录态放在服务端 Session,而是:

  1. 登录成功后签发 token
  2. 后续请求在 Header 里携带 token
  3. 过滤器负责解析 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 常见能力有哪些

比较常见的能力通常包括:

  1. 登录认证
  2. 方法级权限控制
  3. URL 级权限控制
  4. 密码加密
  5. Session 管理
  6. Token 认证接入
  7. 异常处理和未授权响应

如果你想继续把“应用安全”这条线补齐,比较自然的延伸阅读通常是:

  1. 加密、解密与密码存储
  2. 数据脱敏与敏感信息保护

这说明它的价值不是单点功能,而是 把应用安全的常见能力收口成统一框架模型

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 让人觉得重,通常不是因为它设计得乱,而是因为它同时覆盖了:

  1. 认证
  2. 授权
  3. 过滤器链
  4. 安全上下文
  5. 异常处理
  6. 多种认证模式适配

所以它的“重”,本质上是:它在认真处理一整套安全问题,而不是只做一个登录表单。


10. 工程上最常见的几个误区

10.1 误区一:Spring Security 就是登录框架

不是。

登录只是认证入口之一,它真正覆盖的是整条安全链路。

10.2 误区二:用了 JWT 就不需要 Spring Security

JWT 只是认证信息的一种载体,不替代安全框架本身。

10.3 误区三:权限控制只要 Controller 上加注解就够了

接口注解只是授权表达方式之一,真正的安全治理还包括过滤链、上下文、异常响应和认证流程。

10.4 误区四:安全问题只在系统对外开放时才重要

只要系统有用户、有角色、有接口边界,安全问题就已经存在。


11. 一句话总结

Spring Security 的核心价值,是把认证、授权和请求保护统一收口成 Spring 应用中的安全基础设施,而不是让每个接口各自去写零散的安全判断。

基于 VitePress 构建的个人技术笔记。