一个开发者对 Spring Security 拦截机制的深度追问

揭秘Spring Security拦截机制

🚨“为什么我的接口突然 403 了?”

作者:旷野说
关键词:Spring Security、Servlet Filter、403 异常、安全架构、过滤器链、口诀记忆


🧩 一、那个让人抓狂的下午

上周三下午,我正和前端同事联调一个新功能。

他调用 /api/v1/orders/create 接口,返回:

{
  "status": 403,
  "message": "Access Denied"
}

“你是不是没给我开权限?”他问。

我皱眉:“不可能。我用的是管理员账号,Token 是刚登录拿的,而且本地跑得好好的。”

我打开 Postman 重试:

  • /login → 成功,返回 JWT;
  • /api/v1/profile → 成功,返回用户信息;
  • /api/v1/orders/create403,Access Denied

更诡异的是:断点根本没进 Controller
我甚至在 Controller 第一行打了日志,结果日志没输出。

“请求……根本没进来?”我喃喃自语。

那一刻,我意识到:

我的代码不是被业务逻辑拒绝的,而是被某一层“看不见的安全机制”提前拦截了。

而我对这层机制,几乎一无所知。


❓ 二、带着问题,向底层进发

我开始追问自己:

  1. 请求到底是在哪被拦下的?
    是 Spring 的 AOP?还是更早的某个地方?

  2. 为什么有些接口能通,有些不行?
    它们都在同一个 @RestController 里啊!

  3. Spring Security 的 authorizeHttpRequests()@PreAuthorize 到底谁先生效?

  4. 我的 JWT 解析 Filter,真的在权限校验前执行了吗?

这些问题,像一张密网,把我困在“配置能跑,但原理不清”的模糊地带。

于是,我决定:不再把 Spring Security 当成黑盒。我要搞清楚,从请求进来到被拦截,究竟发生了什么


🔍 三、真相的起点:Servlet Filter

经过翻源码、读文档、画流程图,我终于找到了答案的起点——

Spring Security 的安全拦截,根本不在 Spring 的 Bean 层,而是在更底层:Servlet 容器层。

换句话说:
你的请求,甚至还没到达 DispatcherServlet,就已经被一连串 Filter 检查过了。

而这一切,都基于 Java Web 最基础的机制:Servlet Filter


📦 四、什么是 Servlet Filter?

Filter 是 Java EE(现 Jakarta EE)标准的一部分,由 Tomcat、Jetty 等容器管理。

它能在 请求到达目标资源(如 Controller)前,或 响应返回前,对请求/响应进行拦截和处理。

核心方法就一个:

void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
  • 调用 chain.doFilter(...)放行,继续执行;
  • 不调用 → 拦截,直接返回(比如 403)。

⚠️ Filter 是链式执行的,顺序至关重要。


🧱 五、Spring Security 的核心:FilterChainProxy

Spring Security 并没有把十几个安全 Filter 直接注册到 Tomcat,而是把它们全部封装在一个特殊的 Filter 里FilterChainProxy

它像一个“安全中枢”,内部维护了一整条过滤器链:

graph LR
    A[HTTP Request] --> B[FilterChainProxy]
    B --> C[SecurityContextPersistenceFilter]
    B --> D[JwtAuthenticationFilter]     // 我们自定义的
    B --> E[ExceptionTranslationFilter]
    B --> F[FilterSecurityInterceptor]   // 最终权限校验!
    F --> G[DispatcherServlet → Controller]

💡 关键发现
我的 /orders/create 接口之所以 403,就是因为 FilterSecurityInterceptor 拦截了它——
而这时,我的 Controller 根本还没机会执行!


🔑 六、为什么 /profile 能过,/orders/create 不行?

我检查了安全配置:

.authorizeHttpRequests(authz -> authz
    .requestMatchers("/login").permitAll()
    .anyRequest().authenticated()
)

看起来没问题啊?所有接口只要登录就能访问。

但后来我发现:我在 Service 层还加了 @PreAuthorize

@Service
public class OrderService {
    @PreAuthorize("hasRole('ADMIN')")
    public Order createOrder(OrderDto dto) { ... }
}

/profile 没有方法级注解。

真相大白

  • /profile:只经过 Filter 层(URL 权限),通过;
  • /orders/create:先过 Filter 层(通过),再进 AOP 层(方法权限),因无 ADMIN 角色被拒

但为什么没看到异常堆栈?

因为 ExceptionTranslationFilterAccessDeniedException 捕获后,静默转成了 403 响应——这正是 Spring Security 的“优雅”设计,也是调试的难点。


⚖️ 七、Filter vs AOP:职责分明

现在我终于理清了两者的分工:

机制作用层级典型用途是否依赖 Spring
Servlet FilterHTTP 请求层JWT 认证、CORS、日志、URL 权限❌ 不依赖(容器级)
Spring AOP方法调用层@PreAuthorize、事务、缓存✅ 依赖 Spring Bean

Spring Security = Filter(Web 安全) + AOP(方法安全)

  • Filter 拦请求:决定“你能不能访问这个 URL”;
  • AOP 拦方法:决定“你能不能调用这个方法”。

🧠 八、终极口诀:14 个字,牢牢记住

“Filter 拦请求,AOP 拦方法”
“Web 层用 Filter,服务层用 AOP”

  • 遇到 401/403 且 Controller 没执行? → 查 Filter 链(尤其是 FilterSecurityInterceptor)。
  • 遇到 方法被拒但 URL 能通? → 查 @PreAuthorize 和角色权限。

✅ 九、总结:从“能用”到“懂用”

这次 403 异常,像一记警钟,敲醒了我:

不要把框架当魔法。
只有理解底层机制,才能在问题发生时,一眼看穿本质。

现在,当我再看到 Spring Security 的配置:

.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(STATELESS)
.authorizeHttpRequests(...).authenticated()

我不再觉得它只是“模板代码”,而是一个精密的安全流水线——每一步都有其不可替代的作用。

而那个下午的困惑,也成了我深入理解 Web 安全架构的起点。


📚 延伸建议

  • 调试技巧:在 FilterSecurityInterceptorMethodSecurityInterceptor 中打断点;
  • 日志增强:开启 logging.level.org.springframework.security=DEBUG
  • 安全测试:使用 Postman + 不同角色 Token 验证权限边界。

愿你下次再遇 403,不再慌张,而是微微一笑:

“哦,我知道你在哪拦我了。”


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值