Spring Security 与 Spring MVC

一、核心架构:过滤器链 (Filter Chain)

Spring Security 的本质是一条 Servlet 过滤器链 (Filter Chain) 。它在请求到达 Spring MVC 的 DispatcherServlet 之前 就开始工作。

整体流程如下:

关键点 :Security 的所有工作都在 DispatcherServlet 之前完成。


二、Spring Security 认证 (Authentication) 详细过程

认证的目标是回答 "你是谁?" 的问题。

  1. 请求拦截

    • 用户访问一个受保护的资源(例如 /api/user/profile)。
    • 请求首先被 DelegatingFilterProxy 拦截,并委托给 Spring Security 的 FilterChainProxy
  2. 凭证提取

    • 在你的自定义 JwtAuthenticationFilter (通常继承 OncePerRequestFilter) 中,从请求头(如 Authorization: Bearer <token>)中提取 JWT Token。
  3. 凭证验证

    • 调用 JWT 工具类(如 jjwt 库)验证 Token 的签名、有效期等。
    • 如果 Token 无效、过期或缺失,不进行任何操作,直接放行到下一个 Filter。
  4. 身份构建

    • 如果 Token 有效,从中解析出用户标识(如 usernameuserId)。
    • 调用 UserDetailsService.loadUserByUsername() 加载完整的用户信息和权限(UserDetails 对象)。
    • 基于这些信息,创建一个已认证的 Authentication 对象(通常是 UsernamePasswordAuthenticationToken)。
  5. 上下文存储

    • 将这个 Authentication 对象存入 SecurityContext
    • SecurityContextHolder.getContext().setAuthentication(authentication);
    • 这个上下文会在线程内共享,供后续的授权检查使用。
  6. 继续流程

    • 请求继续沿着 Filter 链向下传递。

三、Spring Security 授权 (Authorization) 与 Spring MVC 的协作

授权的目标是回答 "你能做什么?" 的问题。

  1. 授权检查

    • 在 Filter 链的末尾,FilterSecurityInterceptor 会根据你在 HttpSecurity 中配置的规则(如 .anyRequest().authenticated().requestMatchers("/admin/**").hasRole("ADMIN"))进行授权检查。
    • 它会检查 SecurityContext 中是否存在有效的 Authentication 对象及其权限。
  2. 决策点

    • 情况 A:未通过认证/授权
      • 如果 SecurityContext 为空(未登录)或权限不足,ExceptionTranslationFilter 会捕获 AccessDeniedExceptionAuthenticationException
      • 然后,它会调用你配置的 AuthenticationEntryPoint (处理 401) 或 AccessDeniedHandler (处理 403)。
      • 此时,请求根本不会到达 DispatcherServlet 所以你看到的是 401,而不是 404
    • 情况 B:通过认证/授权
      • 请求成功通过整个 Security Filter 链。
      • 请求终于到达 DispatcherServlet
  3. Spring MVC 处理

    • DispatcherServlet 开始工作,尝试根据 URL 找到匹配的 @Controller@RequestMapping
    • 如果找到:执行 Controller 逻辑,返回正常响应。
    • 如果找不到 :抛出 NoHandlerFoundException
    • 这个异常会被你的 @ControllerAdvice 全局异常处理器 (GlobalExceptionHandler) 捕获,并格式化为 404 JSON 响应。

四、关键注意点与最佳实践

  1. 安全优先原则

    • 永远不要为了"先看 404"而绕过 Security 。这会泄露 API 结构,带来严重安全风险。401 for unauthenticated 是正确且安全的行为。
  2. 明确划分公开/私有接口

    • HttpSecurity 配置中,清晰地使用 permitAll() 放行登录、注册、健康检查等公开接口。
    • 使用 authenticated() 或更细粒度的权限控制保护其他所有接口。
  3. 全局异常处理的职责分离

    • Security 层异常 (AuthenticationException, AccessDeniedException):由 Security 自己的 EntryPointAccessDeniedHandler 处理,返回 401/403
    • MVC 层异常 (NoHandlerFoundException, MethodArgumentNotValidException):由 @ControllerAdvice 处理,返回 404/400 等。
    • 业务层异常 :也由 @ControllerAdvice 处理。
  4. 无状态认证 (JWT) 的要点

    • 不要使用 Session :确保 HttpSecurity 配置了 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    • 每次请求都验证 :在自定义 Filter 中,对每个请求都重新验证 Token 并重建 SecurityContext
  5. CORS 与 CSRF

    • 前后端分离项目:务必正确配置 CORS。
    • 无状态 API (JWT) :通常需要禁用 CSRF 保护,因为客户端无法安全地存储和发送 CSRF Token。.csrf().disable()
  6. 密码安全

    • 永远不要明文存储密码。必须使用 PasswordEncoder (如 BCryptPasswordEncoder) 进行加密。
  7. 测试策略

    • 测试 404 时,务必使用有效的 Token。这是验证你的全局异常处理器是否工作的唯一正确方式。
相关推荐
晓晓_za89866810 小时前
Geo 优化源码蒸馏词机制:地域词库构建与匹配逻辑详解
运维·tcp/ip·spring·缓存·ci/cd
xier_ran10 小时前
【infra之路】GPU 存储层次总结:L1 / L2 / HBM
java·网络·数据库
wuminyu11 小时前
JDK21中虚拟线程和FFM协同实现高并发源码剖析
java·linux·c语言·jvm·c++
SWAGGY..11 小时前
【C++进阶】:(1)继承机制详解
java·jvm·c++
码匠许师傅12 小时前
【C++ 面试真题】27. 聊聊 C++ 的内存泄漏与内存布局
java·c++·面试
PPPPickup12 小时前
金证秋招笔试
java·开发语言
不正经学生13 小时前
C语言结构体:自定义数据类型,让变量打包出行
java·c语言·开发语言·数据结构·算法
ycjunhua13 小时前
Spring AI 2.0 GA:ToolCallingAdvisor 重构与 Java Agent 新范式
java·人工智能·spring
开源必胜13 小时前
Future的异常处理以及CompletableFuture传播链、异常处理
java
林森lsjs14 小时前
队列 FIFO 原理 & 手撕两种队列实现(链表 + 循环数组)—数据结构陆
java·开发语言·数据结构·队列