一個開發(fā)者對 Spring Security 攔截機制的深度追問(最新推薦)
??“為什么我的接口突然 403 了?”
?? 一、那個讓人抓狂的下午
上周三下午,我正和前端同事聯(lián)調(diào)一個新功能。
他調(diào)用 /api/v1/orders/create 接口,返回:
{
"status": 403,
"message": "Access Denied"
}“你是不是沒給我開權(quán)限?”他問。
我皺眉:“不可能。我用的是管理員賬號,Token 是剛登錄拿的,而且本地跑得好好的。”
我打開 Postman 重試:
- ?
/login→ 成功,返回 JWT; - ?
/api/v1/profile→ 成功,返回用戶信息; - ?
/api/v1/orders/create→ 403,Access Denied。
更詭異的是:斷點根本沒進 Controller。
我甚至在 Controller 第一行打了日志,結(jié)果日志沒輸出。
“請求……根本沒進來?”我喃喃自語。
那一刻,我意識到:
我的代碼不是被業(yè)務(wù)邏輯拒絕的,而是被某一層“看不見的安全機制”提前攔截了。
而我對這層機制,幾乎一無所知。
? 二、帶著問題,向底層進發(fā)
我開始追問自己:
- 請求到底是在哪被攔下的?
- 是 Spring 的 AOP?還是更早的某個地方?
- 為什么有些接口能通,有些不行?
- 它們都在同一個
@RestController里啊!
- 它們都在同一個
- Spring Security 的
authorizeHttpRequests()和@PreAuthorize到底誰先生效?- 我的 JWT 解析 Filter,真的在權(quán)限校驗前執(zhí)行了嗎?
這些問題,像一張密網(wǎng),把我困在“配置能跑,但原理不清”的模糊地帶。
于是,我決定:不再把 Spring Security 當(dāng)成黑盒。我要搞清楚,從請求進來到被攔截,究竟發(fā)生了什么。
?? 三、真相的起點:Servlet Filter
經(jīng)過翻源碼、讀文檔、畫流程圖,我終于找到了答案的起點——
Spring Security 的安全攔截,根本不在 Spring 的 Bean 層,而是在更底層:Servlet 容器層。
換句話說:
你的請求,甚至還沒到達 DispatcherServlet,就已經(jīng)被一連串 Filter 檢查過了。
而這一切,都基于 Java Web 最基礎(chǔ)的機制:Servlet Filter。
?? 四、什么是 Servlet Filter?
Filter 是 Java EE(現(xiàn) Jakarta EE)標(biāo)準(zhǔn)的一部分,由 Tomcat、Jetty 等容器管理。
它能在 請求到達目標(biāo)資源(如 Controller)前,或 響應(yīng)返回前,對請求/響應(yīng)進行攔截和處理。
核心方法就一個:
void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
- 調(diào)用
chain.doFilter(...)→ 放行,繼續(xù)執(zhí)行; - 不調(diào)用 → 攔截,直接返回(比如 403)。
?? Filter 是鏈?zhǔn)綀?zhí)行的,順序至關(guān)重要。
?? 五、Spring Security 的核心:FilterChainProxy
Spring Security 并沒有把十幾個安全 Filter 直接注冊到 Tomcat,而是把它們全部封裝在一個特殊的 Filter 里:FilterChainProxy。
它像一個“安全中樞”,內(nèi)部維護了一整條過濾器鏈:
graph LR
A[HTTP Request] --> B[FilterChainProxy]
B --> C[SecurityContextPersistenceFilter]
B --> D[JwtAuthenticationFilter] // 我們自定義的
B --> E[ExceptionTranslationFilter]
B --> F[FilterSecurityInterceptor] // 最終權(quán)限校驗!
F --> G[DispatcherServlet → Controller]?? 關(guān)鍵發(fā)現(xiàn):
我的/orders/create接口之所以 403,就是因為FilterSecurityInterceptor攔截了它——
而這時,我的 Controller 根本還沒機會執(zhí)行!
?? 六、為什么/profile能過,/orders/create不行?
我檢查了安全配置:
.authorizeHttpRequests(authz -> authz
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)看起來沒問題啊?所有接口只要登錄就能訪問。
但后來我發(fā)現(xiàn):我在 Service 層還加了 @PreAuthorize!
@Service
public class OrderService {
@PreAuthorize("hasRole('ADMIN')")
public Order createOrder(OrderDto dto) { ... }
}
而 /profile 沒有方法級注解。
? 真相大白:
/profile:只經(jīng)過 Filter 層(URL 權(quán)限),通過;/orders/create:先過 Filter 層(通過),再進 AOP 層(方法權(quán)限),因無 ADMIN 角色被拒!
但為什么沒看到異常堆棧?
因為 ExceptionTranslationFilter 把 AccessDeniedException 捕獲后,靜默轉(zhuǎn)成了 403 響應(yīng)——這正是 Spring Security 的“優(yōu)雅”設(shè)計,也是調(diào)試的難點。
?? 七、Filter vs AOP:職責(zé)分明
現(xiàn)在我終于理清了兩者的分工:
| 機制 | 作用層級 | 典型用途 | 是否依賴 Spring |
|---|---|---|---|
| Servlet Filter | HTTP 請求層 | JWT 認證、CORS、日志、URL 權(quán)限 | ? 不依賴(容器級) |
| Spring AOP | 方法調(diào)用層 | @PreAuthorize、事務(wù)、緩存 | ? 依賴 Spring Bean |
? Spring Security = Filter(Web 安全) + AOP(方法安全)
- Filter 攔請求:決定“你能不能訪問這個 URL”;
- AOP 攔方法:決定“你能不能調(diào)用這個方法”。
?? 八、終極口訣:14 個字,牢牢記住
“Filter 攔請求,AOP 攔方法”
“Web 層用 Filter,服務(wù)層用 AOP”
- 遇到 401/403 且 Controller 沒執(zhí)行? → 查 Filter 鏈(尤其是
FilterSecurityInterceptor)。 - 遇到 方法被拒但 URL 能通? → 查
@PreAuthorize和角色權(quán)限。
? 九、總結(jié):從“能用”到“懂用”
這次 403 異常,像一記警鐘,敲醒了我:
不要把框架當(dāng)魔法。
只有理解底層機制,才能在問題發(fā)生時,一眼看穿本質(zhì)。
現(xiàn)在,當(dāng)我再看到 Spring Security 的配置:
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) .sessionManagement().sessionCreationPolicy(STATELESS) .authorizeHttpRequests(...).authenticated()
我不再覺得它只是“模板代碼”,而是一個精密的安全流水線——每一步都有其不可替代的作用。
而那個下午的困惑,也成了我深入理解 Web 安全架構(gòu)的起點。
?? 延伸建議
- 調(diào)試技巧:在
FilterSecurityInterceptor和MethodSecurityInterceptor中打斷點; - 日志增強:開啟
logging.level.org.springframework.security=DEBUG; - 安全測試:使用 Postman + 不同角色 Token 驗證權(quán)限邊界。
愿你下次再遇 403,不再慌張,而是微微一笑:
“哦,我知道你在哪攔我了。”
到此這篇關(guān)于一個開發(fā)者對 Spring Security 攔截機制的深度追問的文章就介紹到這了,更多相關(guān)Spring Security 攔截機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springBoot靜態(tài)資源加載不到,并且配置了也不生效問題及解決
這篇文章總結(jié)了一個在Spring Boot 2.6.x版本中,由于路徑匹配策略改變導(dǎo)致靜態(tài)資源無法加載的問題,并提供了解決方案:通過配置類或在配置文件中設(shè)置路徑匹配策略為AntPathMatcher,或者直接降級Spring Boot版本2025-02-02
詳解Springboot整合Dubbo之代碼集成和發(fā)布
本篇文章主要介紹了Springboot整合Dubbo之代碼集成和發(fā)布,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
Mybatis實現(xiàn)動態(tài)增刪改查功能的示例代碼
這篇文章主要介紹了Mybatis實現(xiàn)動態(tài)增刪改查功能的示例代碼,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
MybatisPlus實現(xiàn)數(shù)據(jù)攔截的使用示例
在MyBatis-Plus中,可以通過自定義攔截器來實現(xiàn)對SQL語句的攔截和修改,本文就來介紹一下如何使用,具有一定的參考價值,感興趣的可以了解一下2023-10-10
Spring?Boot源碼實現(xiàn)StopWatch優(yōu)雅統(tǒng)計耗時
這篇文章主要為大家介紹了Spring?Boot源碼實現(xiàn)StopWatch優(yōu)雅統(tǒng)計耗時,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
一篇文章帶你解決 IDEA 每次新建項目 maven home directory 總是改變的問題
這篇文章主要介紹了一篇文章帶你解決 IDEA 每次新建項目 maven home directory 總是改變的問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
Java注解機制之Spring自動裝配實現(xiàn)原理詳解
這篇文章主要為大家詳細介紹了Java注解機制之Spring自動裝配實現(xiàn)原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10

