??HttpSecurity?
?中的過濾器順序是怎么維護(hù)的?我想很多開發(fā)者都對(duì)這個(gè)問題感興趣。本篇我和大家一起探討下這個(gè)問題。
??HttpSecurity?
??包含了一個(gè)成員變量??FilterOrderRegistration?
??,這個(gè)類是一個(gè)內(nèi)置過濾器注冊(cè)表。至于這些過濾器的作用,不是本文介紹的重點(diǎn),有興趣可以去看看??FilterOrderRegistration?
?的源碼。
內(nèi)置過濾器的順序
??FilterOrderRegistration?
??維護(hù)了一個(gè)變量??filterToOrder?
??,它記錄了類之間的順序和上下之間的間隔步長(zhǎng)。我們復(fù)制了一個(gè)??FilterOrderRegistration?
?來直觀感受一下過濾器的順序:
CopyFilterOrderRegistration filterOrderRegistration = new CopyFilterOrderRegistration();
// 獲取內(nèi)置過濾器 此方法并未提供
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 ));
打印結(jié)果:
我們可以看得出內(nèi)置過濾器之間的位置是相對(duì)固定的,除了第一個(gè)跟第二個(gè)步長(zhǎng)為??200?
??外,其它步長(zhǎng)為??100?
?。
?
內(nèi)置過濾器并非一定會(huì)生效,僅僅是預(yù)置了它們的排位,需要通過?
?HttpSecurity?
??的??addFilterXXXX?
?系列方法顯式添加才行。
注冊(cè)過濾器的邏輯
??FilterOrderRegistration?
??提供了一個(gè)??put?
?方法:
void put(Class<? extends Filter> filter, int position) {
String className = .getName();
// 如果這個(gè)類已經(jīng)注冊(cè)就忽略
if (this.filterToOrder.containsKey(className)) {
return;
}
// 如果沒有注冊(cè)就注冊(cè)順序。
this.filterToOrder.put(className, position);
}
從這個(gè)方法我們可以得到幾個(gè)結(jié)論:
- 內(nèi)置的?
?34?
?個(gè)過濾器是有固定序號(hào)的,不可被改變。 - 新加入的過濾器的類全限定名是不能和內(nèi)置過濾器重復(fù)的。
- 新加入的過濾器的順序是可以和內(nèi)置過濾器的順序重復(fù)的。
獲取已注冊(cè)過濾器的順序值
??FilterOrderRegistration?
??還提供了一個(gè)??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維護(hù)過濾器的方法
接下來我們分析一下??HttpSecurity?
?維護(hù)過濾器的幾個(gè)方法。
addFilterAtOffsetOf
??addFilterAtOffsetOf?
??是一個(gè)??HttpSecurity?
??的內(nèi)置私有方法。??Filter?
??是想要注冊(cè)到??DefaultSecurityFilterChain?
??中的過濾器,??offset?
??是向右的偏移值,??registeredFilter?
??是已經(jīng)注冊(cè)到??FilterOrderRegistration?
??的過濾器,而且??registeredFilter?
?沒有注冊(cè)的話會(huì)空指針。
private HttpSecurity addFilterAtOffsetOf(Filter filter, int offset, Class<? extends Filter> registeredFilter) {
// 首先會(huì)根據(jù)registeredFilter的順序和偏移值來計(jì)算filter的
int order = this.filterOrders.getOrder(registeredFilter) + offset;
// filter添加到集合中待排序
this.filters.add(new OrderedFilter(filter, order));
// filter注冊(cè)到 FilterOrderRegistration
this.filterOrders.put(filter.getClass(), order);
return this;
}
?
務(wù)必記著?
?registeredFilter?
??一定是已注冊(cè)入??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?
?僅僅是偏移值的區(qū)別,這里不再贅述。
??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?
??必須是已經(jīng)注冊(cè)到??FilterOrderRegistration?
??的??Filter?
??,這意味著它可能是內(nèi)置的??Filter?
??,也可能是先前通過??addFilterBefore?
??、??addFilterAt?
??或者??addFilterAfter?
??注冊(cè)的非內(nèi)置??Filter?
?。
問題來了
之前看到一個(gè)問題,如果??HttpSecurity?
??注冊(cè)兩個(gè)重復(fù)序號(hào)的??Filter?
?會(huì)是怎么樣的順序?我們先來看下排序的機(jī)制:
// filters
private List<OrderedFilter> filters = new ArrayList<>();
//排序
this.filters.sort(OrderComparator.INSTANCE);
看了下??OrderComparator?
?源碼,其實(shí)還是通過order數(shù)字的自然排序,數(shù)字越小越靠前。如果order數(shù)字相同,索引越小越靠前。也就是同樣的序號(hào),誰先??add?
??到??filters?
?誰就越靠前(先add到List中的索引肯定越小)。
另外最近胖哥有很多成系列的內(nèi)容輸出:
OAuth2?系列教程,已經(jīng)更新了40多篇。
- 開源了一個(gè)登錄組件擴(kuò)展spring-security-login-extension,降低對(duì)接配置成本,歡迎學(xué)習(xí)、star。
- 粉絲福利:正版IntelliJ IDEA?抽獎(jiǎng)
有興趣的可以看看。
本文摘自 :https://blog.51cto.com/u