Spring Security addFilter() 順序問題(轉)


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 比較器進行了比較。結果呼之欲出。

二、總結:

  1. FilterComparator 比較器中初始化了Spring Security 自帶的Filter 的順序,即在創建時已經確定了默認Filter的順序。並將所有過濾器保存在一個 filterToOrder Map中。key值是Filter的類名,value是過濾器的順序號。
  2. 當我們調用 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) 會將我們添加的過濾器添加在 filters List集合中, 而在List集合匯總我們手動添加的攔截器在除了 WebAsyncManagerIntegrationFilter 之外的所有系統默認的攔截器之前。
  3. 最后Spring Security 會調用HttpSecurity#performBuild 方法,在這里會使用 FilterComparator 比較器對 filters進行比較排序,序號小的在前,序號大的在后,序號相等則按照原先的filters中的順序。
  4. 由於在 filters List集合中,我們自己添加的過濾器要在除了 WebAsyncManagerIntegrationFilter 之外的所有系統默認的攔截器之前。導致了當我們調用了 HttpSecurity#addFilterAt(A, B.class) 方法時,A攔截器要先於B攔截器執行。

舉例(為了好理解,純屬假設):

  1. 假設Spring Security 里面默認攔截器 有A、B、C 三個(在強調一遍,假設有這三個),那么 FilterComparator 構造函數中就有這三個攔截器的順序值,假設Map值為{“AcName” : 100, “BcName” : 200, “CcName” : 300}。其中AcName是A攔截器的類名,BC同理,100,200,300是他們的序號,用於確定順序。
  2. 我們添加一個攔截器 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)
  3. 在 完成上述步驟后,HttpSecurity 中也會把 F攔截器添加到一個待排序的List集合L中,然后在添加其他系統默認過濾器(相當於這個List保存了所有的過濾器,但是其調用順序未確定,還需要經過排序后才能確定)。最終List集合L就變成了{ A, F, B, C} (之前寫成了F,A,B,C了,經評論區指正,已修改),注意這個是未經排序的過濾器集合,排序后才是真正的調用順序。
    4.在調用 HttpSecurity#performBuild 方法時,會將HttpSecurity 中的過濾器集合L進行排序,排序比較器就是FilterComparator ,排序規則就是誰的排序序號小誰在前,序號大的在后,序號相同的保持 L 中的順序。然后,得出最后的過濾器順序,也就是最終調用順序。

(講的略亂,我已經盡力了。。。)

以上:內容部分木有參考
如有侵擾,聯系刪除。 內容僅用於自我記錄學習使用。如有錯誤,歡迎指正


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM