How is the order of filters in HttpSecurity maintained? I think many developers are interested in this issue. In this article, I will discuss this issue with you.

HttpSecurity contains a member variable FilterOrderRegistration, this class is a built-in filter registry. As for the role of these filters, not the focus of this article, interested to see the FilterOrderRegistration source code.

Built-in filter order

The FilterOrderRegistration maintains a variable filterToOrder that records the order between classes and the interval steps between the top and bottom. We copied a FilterOrderRegistration to visualize the order of the filters.

1
2
3
4
5
6
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 ));

Printed results.

Built-in filter order

We can see that the position between the built-in filters is relatively fixed, except for the first and second step of 200 and the other steps of 100.

Built-in filters do not always take effect, they are simply prepositioned and need to be added explicitly via the addFilterXXXX series of methods in HttpSecurity.

Logic for registering filters

FilterOrderRegistration provides a put method.

1
2
3
4
5
6
7
8
9
 void put(Class<? extends Filter> filter, int position) {
  String className = filter.getName();
        // 如果这个类已经注册就忽略
  if (this.filterToOrder.containsKey(className)) {
   return;
  }
        // 如果没有注册就注册顺序。
  this.filterToOrder.put(className, position);
 }

From this approach we can draw several conclusions.

  • The built-in 34 filters have a fixed serial number and cannot be changed.
  • The class-qualified name of a newly added filter cannot be duplicated with the built-in filter.
  • The order of the newly added filters can be duplicated with the order of the built-in filters.

gets the order value of registered filters

FilterOrderRegistration also provides a getOrder method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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 Methods for Maintaining Filters

Next we analyze a few of the ways in which HttpSecurity maintains filters.

addFilterAtOffsetOf

addFilterAtOffsetOf is a built-in private method of HttpSecurity. Filter is the filter you want to register to the DefaultSecurityFilterChain, offset is the offset to the right, registeredFilter is the filter already registered to the FilterOrderRegistration, and registeredFilter will cause a null pointer exception if it is not registered.

1
2
3
4
5
6
7
8
9
 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;
 }

Always remember that registeredFilter must be a Filter that is registered in FilterOrderRegistration.

addFilter series methods

Here is an example of addFilterAfter.

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

addFilterAfter is to place filter one place after afterFilter, if the order of afterFilter is 400, then the order of filter is 401. The addFilterBefore and addFilterAt logic and addFilterAfter are only differences in offset values, so we won’t go over them here.

The method addFilter is special.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 @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;
 }

A filter must be a Filter that has been registered to FilterOrderRegistration, which means it may be a built-in Filter or a non-built-in Filter that was previously registered with addFilterBefore, addFilterAt or addFilterAfter.

Here comes the question

I saw a question earlier, if HttpSecurity registers two Filters with duplicate serial numbers, what will be the order? Let’s look at the ordering mechanism first.

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

After looking at the OrderComparator source code, it is still sorted by the natural order of order numbers, the smaller the number, the higher it is. If the order numbers are the same, the smaller the index, the higher the order. That is, whoever add to filters first is the first in the same order number ( the smaller the index of the first add to List ).

Reference https://mp.weixin.qq.com/s/14akJwPrzzXWhIfR4Rte5w