Spring Security addFilter() 順序問題
一、 分析
在上面Spring Security + Jwt 項目的搭建過程中,我一直有一個問題。我們假設我們使用addFilterAt(A, B.class)。 即將A攔截器添加到B攔截器的位置。那么addFilterAt 既然沒有覆蓋原先的攔截器,那么A不是在B攔截器前面就是在B攔截器后面,那么豈不是和addFilterBefore 或者 addFilterAfter 重復了?
然而元芳告訴我沒那么簡單。最下面有總結,不想看源碼的小伙伴可以直接看總結。
通過斷點看到。我們通過添加攔截器的代碼是這個樣子的。
那么我們看源碼:
0. FilterComparator
為了更好的理解后面說的內容,我們需要先了解一下 FilterComparator 這個類。顧名思義,這個類是一個過濾器比較器。可以看到如下
其比較的規則如下, getOrder是根據類名來獲取到過濾器的序號。可以看到過濾器的排序是根據排序序號的大小來排序的,序號小的在前,大的災后。
public int compare(Filter lhs, Filter rhs) { Integer left = getOrder(lhs.getClass()); Integer right = getOrder(rhs.getClass()); return left - right; }
- 1
- 2
- 3
- 4
- 5
- 6
我們這里拿 addFilterAt 舉例,addFilterBefore 和 addFilterAfter 類似
1. HttpSecurity#addFilterAt

一句一句看,先看第一句
1.1 this.comparator.registerAt(filter.getClass(), atFilter);
這里的 方法是 在 FilterComparator#registerAt 中
這里可以看到,當我們使用addFilterAt 添加過濾器時,他添加的排序序號是和我們指定的攔截器相同的。()
public void registerAt(Class<? extends Filter> filter, Class<? extends Filter> atFilter) { // 獲取 atFilter 的順序 Integer position = getOrder(atFilter); if (position == null) { throw new IllegalArgumentException( "Cannot register after unregistered Filter " + atFilter); } // 將filter放入 filterToOrder 中, filterToOrder 是一個map集合 put(filter, position); } .... private void put(Class<? extends Filter> filter, int position) { String className = filter.getName(); filterToOrder.put(className, position); } // 調用 addFilterBefore 時使用這個方法注冊,這里可以看到排序序號比指定過濾器要小1 public void registerBefore(Class<? extends Filter> filter, Class<? extends Filter> beforeFilter) { Integer position = getOrder(beforeFilter); if (position == null) { throw new IllegalArgumentException( "Cannot register after unregistered Filter " + beforeFilter); } put(filter, position - 1); } // 調用 addFilterAfter 時使用這個方法注冊,這里可以看到排序序號比指定過濾器要大1 public void registerAfter(Class<? extends Filter> filter, Class<? extends Filter> afterFilter) { Integer position = getOrder(afterFilter); if (position == null) { throw new IllegalArgumentException( "Cannot register after unregistered Filter " + afterFilter); } put(filter, position + 1); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
簡單來說,就是我們 添加的 jwtLoginFilter 攔截器和 UsernamePasswordAuthenticationFilter 的序號是相同的。
1.2 HttpSecurity#addFilter(Filter filter)
代碼很簡單,如下,將添加的過濾器加到 filters 集合中
public HttpSecurity addFilter(Filter filter) { Class<? extends Filter> filterClass = filter.getClass(); if (!comparator.isRegistered(filterClass)) { throw new IllegalArgumentException( "The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."); } this.filters.add(filter); return this; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
通過斷點我們還可以發現,我們手動添加的過濾器是先於大部分系統過濾器被加入到filters集合中的。這一點可以從斷點中看到
另外從 HttpSecurity#performBuild 方法中我們可以知道,filters 在添加完所有過濾器后,使用 FilterComparator 比較器進行了比較。結果呼之欲出。
二、總結:
FilterComparator比較器中初始化了Spring Security 自帶的Filter 的順序,即在創建時已經確定了默認Filter的順序。並將所有過濾器保存在一個filterToOrderMap中。key值是Filter的類名,value是過濾器的順序號。- 當我們調用
HttpSecurity#addFilterAt(A, B.class)方法時(其中B一定是先於A添加,或者B本身就是默認的過濾器),他會將我們的添加的過濾器A在FilterComparator,並給給我們一個和B相同的序號(addFilterBefore(A, B.class) 給A的序號比B小1,addFilterAfter(A, B.class) 給A的序號比B大1)。同時,HttpSecurity#addFilter(Filter filter)會將我們添加的過濾器添加在filtersList集合中, 而在List集合匯總我們手動添加的攔截器在除了WebAsyncManagerIntegrationFilter之外的所有系統默認的攔截器之前。 - 最后Spring Security 會調用
HttpSecurity#performBuild方法,在這里會使用FilterComparator比較器對filters進行比較排序,序號小的在前,序號大的在后,序號相等則按照原先的filters中的順序。 - 由於在
filtersList集合中,我們自己添加的過濾器要在除了WebAsyncManagerIntegrationFilter之外的所有系統默認的攔截器之前。導致了當我們調用了HttpSecurity#addFilterAt(A, B.class)方法時,A攔截器要先於B攔截器執行。
舉例(為了好理解,純屬假設):
- 假設Spring Security 里面默認攔截器 有A、B、C 三個(在強調一遍,假設有這三個),那么
FilterComparator構造函數中就有這三個攔截器的順序值,假設Map值為{“AcName” : 100, “BcName” : 200, “CcName” : 300}。其中AcName是A攔截器的類名,BC同理,100,200,300是他們的序號,用於確定順序。 - 我們添加一個攔截器 F 要調用addFilterAt(F, B.class)。那么它首先會在
FilterComparator中注冊F (保存在排序Map中),排序序號和B相同,也為 200,這時候Map 就變成了{“AcName” : 100, “BcName” : 200, “CcName” : 300, “FcName” : 200}。(如果調用addFilterBefore F的序號就會減1,變成199; 如果調用addFilterAfter F的序號就會加1,變成201) - 在 完成上述步驟后,
HttpSecurity中也會把 F攔截器添加到一個待排序的List集合L中,然后在添加其他系統默認過濾器(相當於這個List保存了所有的過濾器,但是其調用順序未確定,還需要經過排序后才能確定)。最終List集合L就變成了{ A, F, B, C} (之前寫成了F,A,B,C了,經評論區指正,已修改),注意這個是未經排序的過濾器集合,排序后才是真正的調用順序。
4.在調用HttpSecurity#performBuild方法時,會將HttpSecurity中的過濾器集合L進行排序,排序比較器就是FilterComparator,排序規則就是誰的排序序號小誰在前,序號大的在后,序號相同的保持 L 中的順序。然后,得出最后的過濾器順序,也就是最終調用順序。
(講的略亂,我已經盡力了。。。)
以上:內容部分木有參考
如有侵擾,聯系刪除。 內容僅用於自我記錄學習使用。如有錯誤,歡迎指正
