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。这是验证你的全局异常处理器是否工作的唯一正确方式。
相关推荐
nanxun8861 天前
记一次诡异的 Docker 容器"串包"故障排查
java
用户1563068103511 天前
Day01 | Java 基础(Java SE)
java
行者全栈架构师1 天前
Maven dependency:tree 的 8 个高级用法
java·后端
行者全栈架构师1 天前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端
令人头秃的代码0_01 天前
mac(m5)平台编译openjdk
java
唐青枫2 天前
Java JDBC 实战指南:从 Connection 到事务和连接池
java
一个做软件开发的牛马2 天前
MyBatis-Plus 从零实战:完整搭建可运行 Demo,BaseMapper 零 SQL、Wrapper 条件构造、分页插件与代码生成器详解
java·后端
用户3721574261352 天前
Java 处理 PDF 图片:提取 PDF 中的图片,并压缩 PDF 图片体积
java
用户3721574261352 天前
Java 打印 Word 文档:从基础打印到高级设置
java
用户3521802454753 天前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程