首頁 > 軟體

Spring Security內建過濾器的維護方法

2022-02-22 16:00:03

Spring Security中的內建過濾器順序是怎麼維護的?我想很多開發者都對這個問題感興趣。本篇我和大家一起探討下這個問題。

HttpSecurity包含了一個成員變數FilterOrderRegistration,這個類是一個內建過濾器登入檔。至於這些過濾器的作用,不是本文介紹的重點,有興趣可以去看看FilterOrderRegistration的原始碼。

內建過濾器的順序

FilterOrderRegistration維護了一個變數filterToOrder,它記錄了類之間的順序和上下之間的間隔步長。我們複製了一個FilterOrderRegistration來直觀感受一下過濾器的順序:

CopyFilterOrderRegistration filterOrderRegistration = new CopyFilterOrderRegistration();
        // 獲取內建過濾器  此方法並未提供
        Map<String, Integer> filterToOrder = filterOrderRegistration.getFilterToOrder();
        TreeMap<Integer, String> orderToFilter = new TreeMap<>();
        filterToOrder.forEach((name, order) -> orderToFilter.put(order,name));
        orderToFilter.forEach((order,name) -> System.out.println(" 順序:" + order+" 類名:" + name ));

列印結果:

我們可以看得出內建過濾器之間的位置是相對固定的,除了第一個跟第二個步長為200外,其它步長為100。

內建過濾器並非一定會生效,僅僅是預置了它們的排位,需要通過HttpSecurity的addFilterXXXX系列方法顯式新增才行。

註冊過濾器的邏輯

FilterOrderRegistration提供了一個put方法:

 void put(Class<? extends Filter> filter, int position) {
  String className = filter.getName();
        // 如果這個類已經註冊就忽略
  if (this.filterToOrder.containsKey(className)) {
   return;
  }
        // 如果沒有註冊就註冊順序。
  this.filterToOrder.put(className, position);
 }

從這個方法我們可以得到幾個結論:

  • 內建的34個過濾器是有固定序號的,不可被改變。
  • 新加入的過濾器的類全限定名是不能和內建過濾器重複的。
  • 新加入的過濾器的順序是可以和內建過濾器的順序重複的。

獲取已註冊過濾器的順序值

FilterOrderRegistration還提供了一個getOrder方法:

  Integer getOrder(Class<?> clazz) {
        // 如果類Class 或者 父類別Class 名為空就返回null
        while (clazz != null) {
            Integer result = this.filterToOrder.get(clazz.getName());
            // 如果獲取到順序值就返回
            if (result != null) {
                return result;
            }
            // 否則嘗試去獲取父類別的順序值
            clazz = clazz.getSuperclass();
        }
        return null;
    }

HttpSecurity維護過濾器的方法

接下來我們分析一下HttpSecurity維護過濾器的幾個方法。

addFilterAtOffsetOf

addFilterAtOffsetOf是一個HttpSecurity的內建私有方法。Filter是想要註冊到DefaultSecurityFilterChain中的過濾器,offset是向右的偏移值,registeredFilter是已經註冊到FilterOrderRegistration的過濾器,而且registeredFilter沒有註冊的話會空指標。

 private HttpSecurity addFilterAtOffsetOf(Filter filter, int offset, Class<? extends Filter> registeredFilter) {
        // 首先會根據registeredFilter的順序和偏移值來計算filter的
  int order = this.filterOrders.getOrder(registeredFilter) + offset;
        // filter新增到集合中待排序
  this.filters.add(new OrderedFilter(filter, order));
        // filter註冊到 FilterOrderRegistration
  this.filterOrders.put(filter.getClass(), order);
  return this;
 }

務必記著registeredFilter一定是已註冊入FilterOrderRegistration的Filter。

addFilter系列方法

這裡以addFilterAfter為例。

 @Override
 public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
  return addFilterAtOffsetOf(filter, 1, afterFilter);
 }

addFilterAfter是將filter的位置置於afterFilter後一位,假如afterFilter順序值為400,則filter順序值為401。addFilterBefore和addFilterAt邏輯和addFilterAfter僅僅是偏移值的區別,這裡不再贅述。

addFilter的方法比較特殊:

 @Override
 public HttpSecurity addFilter(Filter filter) {
  Integer order = this.filterOrders.getOrder(filter.getClass());
  if (order == null) {
   throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
     + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
  }
  this.filters.add(new OrderedFilter(filter, order));
  return this;
 }

filter必須是已經註冊到FilterOrderRegistration的Filter,這意味著它可能是內建的Filter,也可能是先前通過addFilterBefore、addFilterAt或者addFilterAfter註冊的非內建Filter。

問題來了

之前看到一個問題,如果HttpSecurity註冊兩個重複序號的Filter會是怎麼樣的順序呢?我們先來看下排序的機制:

// filters
private List<OrderedFilter> filters = new ArrayList<>(); 
//排序
this.filters.sort(OrderComparator.INSTANCE);

看了下OrderComparator原始碼,其實還是通過數位的自然排序,數位越小越靠前。如果數位相同,索引越小越靠前。也就是同樣的序號,誰先add到filters誰就越靠前。

到此這篇關於Spring Security的內建過濾器是如何維護的的文章就介紹到這了,更多相關Spring Security內建過濾器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com