JavaSpring Security
HttpSecurity包含了一个成员变量FilterOrderRegistration,这个类是一个内置过滤器注册表。至于这些过滤器的作用,不是本文介绍的重点,有兴趣可以去看看FilterOrderRegistration的源码。

内置过滤器的顺序

FilterOrderRegistration维护了一个变量filterToOrder,它记录了类之间的顺序和上下之间的间隔步长。复制了一个FilterOrderRegistration来直观感受一下过滤器的顺序:

  1. CopyFilterOrderRegistration filterOrderRegistration = new CopyFilterOrderRegistration();
  2. // 获取内置过滤器 此方法并未提供
  3. Map<String, Integer> filterToOrder = filterOrderRegistration.getFilterToOrder();
  4. TreeMap<Integer, String> orderToFilter = new TreeMap<>();
  5. filterToOrder.forEach((name, order) -> orderToFilter.put(order,name));
  6. orderToFilter.forEach((order,name) -> System.out.println(" 顺序:" + order+" 类名:" + name ));

打印结果:
Spring Security过滤器的排序规则 - 图1
可以看得出内置过滤器之间的位置是相对固定的,除了第一个跟第二个步长为200外,其它步长为100。
内置过滤器并非一定会生效,仅仅是预置了它们的排位,需要通过HttpSecurityaddFilterXXXX系列方法显式添加才行。

注册过滤器的逻辑

FilterOrderRegistration提供了一个put方法:

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

从这个方法可以得到几个结论:

  • 内置的34个过滤器是有固定序号的,不可被改变。
  • 新加入的过滤器的类全限定名是不能和内置过滤器重复的。
  • 新加入的过滤器的顺序是可以和内置过滤器的顺序重复的。

    获取已注册过滤器的顺序值

    FilterOrderRegistration还提供了一个getOrder方法:
    1. Integer getOrder(Class<?> clazz) {
    2. // 如果类Class 或者 父类Class 名为空就返回null
    3. while (clazz != null) {
    4. Integer result = this.filterToOrder.get(clazz.getName());
    5. // 如果获取到顺序值就返回
    6. if (result != null) {
    7. return result;
    8. }
    9. // 否则尝试去获取父类的顺序值
    10. clazz = clazz.getSuperclass();
    11. }
    12. return null;
    13. }

    HttpSecurity维护过滤器的方法

    接下来分析一下HttpSecurity维护过滤器的几个方法。

    addFilterAtOffsetOf

    addFilterAtOffsetOf是一个HttpSecurity的内置私有方法。Filter是想要注册到DefaultSecurityFilterChain中的过滤器,offset是向右的偏移值,registeredFilter是已经注册到FilterOrderRegistration的过滤器,而且registeredFilter没有注册的话会空指针。
    1. private HttpSecurity addFilterAtOffsetOf(Filter filter, int offset, Class<? extends Filter> registeredFilter) {
    2. // 首先会根据registeredFilter的顺序和偏移值来计算filter的
    3. int order = this.filterOrders.getOrder(registeredFilter) + offset;
    4. // filter添加到集合中待排序
    5. this.filters.add(new OrderedFilter(filter, order));
    6. // filter注册到 FilterOrderRegistration
    7. this.filterOrders.put(filter.getClass(), order);
    8. return this;
    9. }
    务必记着registeredFilter一定是已注册入FilterOrderRegistrationFilter

    addFilter系列方法

    这里以addFilterAfter为例。
    1. @Override
    2. public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
    3. return addFilterAtOffsetOf(filter, 1, afterFilter);
    4. }
    addFilterAfter是将filter的位置置于afterFilter后一位,假如afterFilter顺序值为400,则filter顺序值为401。addFilterBeforeaddFilterAt逻辑和addFilterAfter仅仅是偏移值的区别,这里不再赘述。
    addFilter的方法比较特殊:
    1. @Override
    2. public HttpSecurity addFilter(Filter filter) {
    3. Integer order = this.filterOrders.getOrder(filter.getClass());
    4. if (order == null) {
    5. throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
    6. + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
    7. }
    8. this.filters.add(new OrderedFilter(filter, order));
    9. return this;
    10. }
    filter必须是已经注册到FilterOrderRegistrationFilter,这意味着它可能是内置的Filter,也可能是先前通过addFilterBeforeaddFilterAt或者addFilterAfter注册的非内置Filter

    问题来了

    之前看到一个问题,如果HttpSecurity注册两个重复序号的Filter会是怎么样的顺序?先来看下排序的机制:
    1. // filters
    2. private List<OrderedFilter> filters = new ArrayList<>();
    3. //排序
    4. this.filters.sort(OrderComparator.INSTANCE);
    看了下OrderComparator源码,其实还是通过order数字的自然排序,数字越小越靠前。如果order数字相同,索引越小越靠前。也就是同样的序号,谁先addfilters谁就越靠前(先add到List中的索引肯定越小)。