Spring Security 可以為 url 設置各種訪問規則,比如:
http.authorizeRequests().antMatchers("/api/**").denyAll(); //拒絕訪問 http.authorizeRequests().antMatchers("/api/**").authenticated(); //需認證通過 http.authorizeRequests().antMatchers("/api/**").permitAll(); //無條件允許訪問
但是遇到下面這些情況,SpringSecurity會如何處理呢?
1、一個url可以匹配多個規則:如 /api/bbb/ccc 這個url ,既可以匹配 /** ,又可以匹配 /api/**,最終會匹配哪條規則呢?
2、存在相同url 的匹配規則,如上面例子中 "/api/**" 一共有三條規則,一個denyAll,一個authenticated,一個permitAll ,最終會匹配哪條規則呢?
讓我們深入代碼弄清楚這些問題【版本 SpringSecurity 5.0.7】:
每注冊一個規則 antMatchers("/api/**").xxx() 時,
SpringSecurity會把這個規則按注冊先后順序放到一個ArrayList<UrlMapping>中,先注冊的規則放前面,后注冊的放后面。
然后將這個 ArrayList<UrlMapping> 進行處理( AbstractConfigAttributeRequestMatcherRegistry.createRequestMap 方法)。
處理的規則是這樣: 按順序遍歷這個ArrayList,以 url的RequestMatcher 為 key ,以 denyAll,authenticated 等 為值, put 進一個 LinkedHashMap。
這個 LinkedHashMap 就是最終的 規則集合。
此時,有訪問請求Web服務器,
Web服務器從request中取出訪問的url。
通過之前得到的那個 LinkedHashMap 去 對 url 進行權限判斷,第一個能匹配上這個url的規則,就是最終會執行的規則。(DefaultFilterInvocationSecurityMetadataSource.getAttributes 方法)
上面的內容可能看起來有點暈,簡單說就是:
1、注冊的規則 按先后順序放到一個List, 然后又用Map去掉完全相同的 url匹配規則。這個Map 就是最終的規則存放處。
2、當有訪問到來時,則根據訪問的url 循環這個 Map 進行匹配,能匹配上的第一個規則就是 最終執行的規則。
然后回到我們上面提出的兩個問題:
1、一個url可以匹配多個規則:如 /api/bbb/ccc 這個url ,既可以匹配 /** ,又可以匹配 /api/**,最終會匹配哪條規則呢?
解答:按你注冊規則的順序,第一條能匹配上的,就是最終會執行的規則。
延伸:注冊規則的時候一定要注意順序,如果 /** 第一個注冊,則后面的任何規則都不會被匹配了,永遠執行 /** 的這個規則。
2、存在相同url 的匹配規則,如上面例子中 "/api/**" 一共有三條規則,一個denyAll,一個authenticated,一個permitAll ,最終會匹配哪條規則呢?
解答:在 ArrayList 轉 LinkedHashMap 時,相同的url 的匹配規則中 后面的會沖掉 前面的, 所以,最終執行的規則會是最后注冊的那條。
最后看一個實際的例子:
注冊完規則的那個 LinkedHashMap :
一共注冊了9條規則,我們注意到有三條相同的url 的匹配規則
最后轉成 LinkedHashMap 后:
Map中只有7條規則了,因為之前9條中的3條規則 url 相同,被處理掉了。
/Dev/** 是 第3,第7,第9 注冊的,當合並時, 用的第9的值是好理解的,但要特別注意,它在 Map中的順序是第3的位置,過濾優先級很高的。