Spring Security如何在Servlet中執(zhí)行
Spring Security 是一個(gè)強(qiáng)大的認(rèn)證和授權(quán)框架,它的使用方式也非常簡(jiǎn)單,但是要想真正理解它就需要花一時(shí)間來學(xué)習(xí)了,最近在學(xué)習(xí) Spring Security 時(shí)有一些新的理解,特意記錄下來防止知識(shí)忘記的太快,畢竟好記性不如爛筆關(guān),也給即將準(zhǔn)備學(xué)習(xí) Spring Security 的同志做一個(gè)參考。
由于我在學(xué)習(xí)和使用是基于 Servlet Applications 的,所以文中的大部分都與 Servlet 相關(guān),當(dāng)然 Spring Security 還支持 Reactive Applications 功能上都是一樣,在架構(gòu)上會(huì)有一些差別,有興趣的同學(xué)可以自行查看官方文檔。
Spring Securty 在 Servlet Applications 中的應(yīng)用
以下部分內(nèi)容摘自官方文檔
Servlet Filter Chain
提到 Servlet Filter Chain 應(yīng)該都熟悉的吧,它們是一系列由 javax.servlet.Filter 實(shí)現(xiàn)類組成的一個(gè)鏈,大致圖如下所示:
上圖中Client發(fā)送Http請(qǐng)求,然后請(qǐng)求經(jīng)過FilterChain,每個(gè)匹配的Filter都有機(jī)會(huì)處理request和response對(duì)象,最終請(qǐng)求會(huì)到達(dá)servlet(如何filter中沒有特殊處理的情況下)。
Spring Security 的實(shí)現(xiàn)簡(jiǎn)單來說,就是往Servlet Filter Chain加了一個(gè)特殊的過濾器來處理認(rèn)證或授權(quán)請(qǐng)求 。
DelegatingFilterProxy
Spring 提供一個(gè)javax.servlet.Filter的實(shí)現(xiàn)類 DelegatingFilterProxy ,它的主要功能跟它的名稱一樣,通過代理模式委托給一個(gè)Spring管理的Bean來完成相應(yīng)的功能。
在上圖中,DelegatingFilterProxy 會(huì)在 ApplicationContext 中查找 Filter0 并執(zhí)行Filter0的doFilter方法:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { // Lazily get Filter that was registered as a Spring Bean // For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0 Filter delegate = getFilterBean(someBeanName); // delegate work to the Spring Bean delegate.doFilter(request, response); }
FilterChainProxy
前面說過DelegatingFilterProxy只是一個(gè)代理 Filter,并沒有真正的功能。
在 Spring Security 中還有一個(gè) FilterChainProxy 類,它是 Spring Security 中非常重要的入口(斷點(diǎn)打在這準(zhǔn)沒錯(cuò)),它負(fù)責(zé)匹配請(qǐng)求、執(zhí)行 Filter 等功能。
你可能發(fā)現(xiàn)了上圖中在FilterChainProxy部分還有個(gè) SecurityFilterChain,它是一個(gè)接口只有兩個(gè)方法:
- matches用于匹配請(qǐng)求
- getFilters是獲取針對(duì)匹配請(qǐng)求的所有的 Filters
SecurityFilterChain 接口:
public interface SecurityFilterChain { boolean matches(HttpServletRequest request); List<Filter> getFilters(); }
SecurityFilterChain
SecurityFilterChain 里面包含很多個(gè) Filter ,不同的 Filter 完成不同的功能,如登陸認(rèn)證、退出登陸、設(shè)置SecurityContext等,在Spring Security 中可以有多個(gè) SecurityFilterChain 每個(gè) SecurityFilterChain 負(fù)責(zé)不同的請(qǐng)求地址,如可以針對(duì)/app/api/**與/web/api/**設(shè)置不同的認(rèn)證規(guī)則。
總結(jié)
前面提到了四個(gè)重要的概念:
- Servlet Filter Chain:Serverl過濾器鏈
- DelegatingFilterProxy:Spring Filter代理類,將功能委托給 FilterChainProxy
- FilterChainProxy:匹配請(qǐng)求,執(zhí)行 SecurityFilterChain 中的過濾器
- SecurityFilterChain:包含一組Filter
總結(jié)下來可以用一張圖表示:
根據(jù)上面圖如Client訪問/web/api/login就會(huì)匹配到SecurityFilterChain 0并執(zhí)行其中的 Filters。
匹配過程我看了下FilterChainProxy的源碼,大致流程和我理解的差不多,我把代碼精簡(jiǎn)了一下以下:
public class FilterChainProxy extends GenericFilterBean { private List<SecurityFilterChain> filterChains; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ... doFilterInternal(request, response, chain); ... } private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ... List<Filter> filters = getFilters(fwRequest); ... VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters); vfc.doFilter(fwRequest, fwResponse); } private List<Filter> getFilters(HttpServletRequest request) { for (SecurityFilterChain chain : filterChains) { if (chain.matches(request)) { return chain.getFilters(); } } return null; } private static class VirtualFilterChain implements FilterChain { @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { ... } }
首先在FilterChainProxy的doFilter方法會(huì)執(zhí)行doFilterInternal方法
doFilterInternal 方法中調(diào)用 getFilters 獲取過濾器列表
private List<Filter> getFilters(HttpServletRequest request) { for (SecurityFilterChain chain : filterChains) { if (chain.matches(request)) { return chain.getFilters(); } } return null; }
在 getFilters 會(huì)調(diào)用SecurityFilterChain.matches匹配請(qǐng)求
最后將得到的filters放在 VirtualFilterChain 中執(zhí)行最后
這篇文章主要講述了 Spring Securty 與 Servlet Applications 集成部分
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
單元測(cè)試 @mock與@SpringBootTest的使用
這篇文章主要介紹了單元測(cè)試 @mock與@SpringBootTest的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Spring Boot緩存實(shí)戰(zhàn) Caffeine示例
本篇文章主要介紹了Spring Boot緩存實(shí)戰(zhàn) Caffeine示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02Java的中l(wèi)ombok下的@Builder注解用法詳解
這篇文章主要介紹了Java的中l(wèi)ombok下的@Builder注解用法詳解,lombok注解在java進(jìn)行編譯時(shí)進(jìn)行代碼的構(gòu)建,對(duì)于java對(duì)象的創(chuàng)建工作它可以更優(yōu)雅,不需要寫多余的重復(fù)的代碼,在出現(xiàn)lombok之后,對(duì)象的創(chuàng)建工作更提供Builder方法,需要的朋友可以參考下2023-11-11Java代碼實(shí)現(xiàn)矩形覆蓋實(shí)例
這篇文章主要介紹了Java代碼實(shí)現(xiàn)矩形覆蓋實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06Java,C#使用二進(jìn)制序列化、反序列化操作數(shù)據(jù)
這篇文章主要介紹了Java,C#使用二進(jìn)制序列化、反序列化操作數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2014-10-10詳解SpringBoot中JdbcTemplate的事務(wù)控制
JdbcTemplate是spring-jdbc提供的數(shù)據(jù)庫核心操作類,那對(duì)JdbcTemplate進(jìn)行事務(wù)控制呢,本文就詳細(xì)的介紹一下2021-09-09Java集合類的組織結(jié)構(gòu)和繼承、實(shí)現(xiàn)關(guān)系詳解
這篇文章主要介紹了Java集合類的組織結(jié)構(gòu)和繼承、實(shí)現(xiàn)關(guān)系,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11