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 有效,从中解析出用户标识(如 username 或 userId)。
    • 调用 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 会捕获 AccessDeniedException 或 AuthenticationException。
      • 然后,它会调用你配置的 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 自己的 EntryPoint 和 AccessDeniedHandler 处理,返回 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。这是验证你的全局异常处理器是否工作的唯一正确方式。
相关推荐
BD_Marathon8 分钟前
消息对象中字段的说明
java·前端·python
zlpzlpzyd19 分钟前
nacos的openapi地址
java
落魄实习生1 小时前
Agent Scope Java 2.x 系列【10】Middleware
java·开发语言·ai
ZealSinger2 小时前
Spring7 RestTemplate弃用迁移
java·spring·后端开发·restclient
朝朝辞暮i2 小时前
C++ 第 13 课:值传递 —— 为什么函数里改了,外面却没变?
java·c++·算法
弹简特2 小时前
【Java项目-企悦抽】16-抽奖模块01-获取活动完整信息接口实现
java·开发语言·状态模式·springboot
萧瑟余晖3 小时前
Spring 资源与环境配置体系详解
spring
泡茶喝茶写代码3 小时前
A股量化数据工程:从 REST 接口到策略信号(第 1 篇):指数列表与实时行情接入
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据接口
Sarvartha3 小时前
接口基础知识
java·开发语言
不才不才不不才4 小时前
Spring 源码系列(29): 20 道高频面试题源码级解析合集
java·后端·spring