HttpSecurity 也是 Spring Security 中的重要一環。咱們平時所作的大部分 Spring Security 配置也都是基於 HttpSecurity 來配置的。所以咱們有必要從源碼的角度來理解下 HttpSecurity 到底幹了啥?java
首先咱們來看下 HttpSecurity 的繼承關係圖:app
能夠看到,HttpSecurity 繼承自 AbstractConfiguredSecurityBuilder,同時實現了 SecurityBuilder 和 HttpSecurityBuilder 兩個接口。cors
咱們來看下 HttpSecurity 的定義:ide
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity> implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> { //... }
這裏每個類都帶有泛型,看得人有點眼花繚亂。ui
我把這個泛型類拿出來和你們講一下,小夥伴們就明白了。this
泛型主要是兩個,DefaultSecurityFilterChain 和 HttpSecurity,HttpSecurity 就不用說了,這是咱們今天的主角,那麼 DefaultSecurityFilterChain 是幹嗎的?spa
這咱們就得從 SecurityFilterChain 提及了。code
先來看定義:orm
public interface SecurityFilterChain { boolean matches(HttpServletRequest request); List<Filter> getFilters(); }
SecurityFilterChain 其實就是咱們平時所說的 Spring Security 中的過濾器鏈,它裏邊定義了兩個方法,一個是 matches 方法用來匹配請求,另一個 getFilters 方法返回一個 List 集合,集合中放着 Filter 對象,當一個請求到來時,用 matches 方法去比較請求是否和當前鏈吻合,若是吻合,就返回 getFilters 方法中的過濾器,那麼當前請求會逐個通過 List 集合中的過濾器。這一點,小夥伴們能夠回憶前面【深刻理解 FilterChainProxy【源碼篇】】一文。csrf
SecurityFilterChain 接口只有一個實現類,那就是 DefaultSecurityFilterChain:
public final class DefaultSecurityFilterChain implements SecurityFilterChain { private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class); private final RequestMatcher requestMatcher; private final List<Filter> filters; public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) { this(requestMatcher, Arrays.asList(filters)); } public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) { logger.info("Creating filter chain: " + requestMatcher + ", " + filters); this.requestMatcher = requestMatcher; this.filters = new ArrayList<>(filters); } public RequestMatcher getRequestMatcher() { return requestMatcher; } public List<Filter> getFilters() { return filters; } public boolean matches(HttpServletRequest request) { return requestMatcher.matches(request); } @Override public String toString() { return "[ " + requestMatcher + ", " + filters + "]"; } }
DefaultSecurityFilterChain 只是對 SecurityFilterChain 中的方法進行了實現,並無特別值得說的地方,鬆哥也就不囉嗦了。
那麼從上面的介紹中,你們能夠看到,DefaultSecurityFilterChain 其實就至關因而 Spring Security 中的過濾器鏈,一個 DefaultSecurityFilterChain 表明一個過濾器鏈,若是系統中存在多個過濾器鏈,則會存在多個 DefaultSecurityFilterChain 對象。
接下來咱們把 HttpSecurity 的這幾個父類捋一捋。
public interface SecurityBuilder<O> { O build() throws Exception; }
SecurityBuilder 就是用來構建過濾器鏈的,在 HttpSecurity 實現 SecurityBuilder 時,傳入的泛型就是 DefaultSecurityFilterChain,因此 SecurityBuilder#build 方法的功能很明確,就是用來構建一個過濾器鏈出來。
HttpSecurityBuilder 看名字就是用來構建 HttpSecurity 的。不過它也只是一個接口,具體的實如今 HttpSecurity 中,接口定義以下:
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends SecurityBuilder<DefaultSecurityFilterChain> { <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer( Class<C> clazz); <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer( Class<C> clazz); <C> void setSharedObject(Class<C> sharedType, C object); <C> C getSharedObject(Class<C> sharedType); H authenticationProvider(AuthenticationProvider authenticationProvider); H userDetailsService(UserDetailsService userDetailsService) throws Exception; H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter); H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter); H addFilter(Filter filter); }
這裏的方法比較簡單:
這即是 HttpSecurityBuilder 中的功能,這些接口在 HttpSecurity 中都將獲得實現。
AbstractSecurityBuilder 類實現了 SecurityBuilder 接口,該類中主要作了一件事,就是確保整個構建只被構建一次。
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> { private AtomicBoolean building = new AtomicBoolean(); private O object; public final O build() throws Exception { if (this.building.compareAndSet(false, true)) { this.object = doBuild(); return this.object; } throw new AlreadyBuiltException("This object has already been built"); } public final O getObject() { if (!this.building.get()) { throw new IllegalStateException("This object has not been built"); } return this.object; } protected abstract O doBuild() throws Exception; }
能夠看到,這裏從新定義了 build 方法,並設置 build 方法爲 final 類型,沒法被重寫,在 build 方法中,經過 AtomicBoolean 實現該方法只被調用一次。具體的構建邏輯則定義了新的抽象方法 doBuild,未來在實現類中經過 doBuild 方法定義構建邏輯。
AbstractSecurityBuilder 方法的實現類就是 AbstractConfiguredSecurityBuilder。
AbstractConfiguredSecurityBuilder 中所作的事情就比較多了,咱們分別來看。
首先 AbstractConfiguredSecurityBuilder 中定義了一個枚舉類,將整個構建過程分爲 5 種狀態,也能夠理解爲構建過程生命週期的五個階段,以下:
private enum BuildState { UNBUILT(0), INITIALIZING(1), CONFIGURING(2), BUILDING(3), BUILT(4); private final int order; BuildState(int order) { this.order = order; } public boolean isInitializing() { return INITIALIZING.order == order; } public boolean isConfigured() { return order >= CONFIGURING.order; } }
五種狀態分別是 UNBUILT、INITIALIZING、CONFIGURING、BUILDING 以及 BUILT。另外還提供了兩個判斷方法,isInitializing 判斷是否正在初始化,isConfigured 表示是否已經配置完畢。
AbstractConfiguredSecurityBuilder 中的方法比較多,鬆哥在這裏列出來兩個關鍵的方法和你們分析:
private <C extends SecurityConfigurer<O, B>> void add(C configurer) { Assert.notNull(configurer, "configurer cannot be null"); Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer .getClass(); synchronized (configurers) { if (buildState.isConfigured()) { throw new IllegalStateException("Cannot apply " + configurer + " to already built object"); } List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers .get(clazz) : null; if (configs == null) { configs = new ArrayList<>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } } private Collection<SecurityConfigurer<O, B>> getConfigurers() { List<SecurityConfigurer<O, B>> result = new ArrayList<>(); for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) { result.addAll(configs); } return result; }
第一個就是這個 add 方法,這至關因而在收集全部的配置類。將全部的 xxxConfigure 收集起來存儲到 configurers 中,未來再統一初始化並配置,configurers 自己是一個 LinkedHashMap ,key 是配置類的 class,value 是一個集合,集合裏邊放着 xxxConfigure 配置類。當須要對這些配置類進行集中配置的時候,會經過 getConfigurers 方法獲取配置類,這個獲取過程就是把 LinkedHashMap 中的 value 拿出來,放到一個集合中返回。
另外一個方法就是 doBuild 方法。
@Override protected final O doBuild() throws Exception { synchronized (configurers) { buildState = BuildState.INITIALIZING; beforeInit(); init(); buildState = BuildState.CONFIGURING; beforeConfigure(); configure(); buildState = BuildState.BUILDING; O result = performBuild(); buildState = BuildState.BUILT; return result; } } private void init() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.init((B) this); } for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) { configurer.init((B) this); } } private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.configure((B) this); } }
在 AbstractSecurityBuilder 類中,過濾器的構建被轉移到 doBuild 方法上面了,不過在 AbstractSecurityBuilder 中只是定義了抽象的 doBuild 方法,具體的實如今 AbstractConfiguredSecurityBuilder。
doBuild 方法就是一邊更新狀態,進行進行初始化。
beforeInit 是一個預留方法,沒有任何實現。
init 方法就是找到全部的 xxxConfigure,挨個調用其 init 方法進行初始化。
beforeConfigure 是一個預留方法,沒有任何實現。
configure 方法就是找到全部的 xxxConfigure,挨個調用其 configure 方法進行配置。
最後則是 performBuild 方法,是真正的過濾器鏈構建方法,可是在 AbstractConfiguredSecurityBuilder 中 performBuild 方法只是一個抽象方法,具體的實如今 HttpSecurity 中。
這即是 HttpSecurity 全部父類、父接口的功能。
看完了父輩,接下來回到咱們今天文章的主題,HttpSecurity。
HttpSecurity 作的事情,就是進行各類各樣的 xxxConfigurer 配置。
隨便舉幾例:
public CorsConfigurer<HttpSecurity> cors() throws Exception { return getOrApply(new CorsConfigurer<>()); } public CsrfConfigurer<HttpSecurity> csrf() throws Exception { ApplicationContext context = getContext(); return getOrApply(new CsrfConfigurer<>(context)); } public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception { return getOrApply(new ExceptionHandlingConfigurer<>()); }
HttpSecurity 中有大量相似的方法,過濾器鏈中的過濾器就是這樣一個一個配置的。我就不一一介紹了。
每一個配置方法的結尾都會來一句 getOrApply,這個是幹嗎的?
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply( C configurer) throws Exception { C existingConfig = (C) getConfigurer(configurer.getClass()); if (existingConfig != null) { return existingConfig; } return apply(configurer); }
getConfigurer 方法是在它的父類 AbstractConfiguredSecurityBuilder 中定義的,目的就是去查看當前這個 xxxConfigurer 是否已經配置過了。
若是當前 xxxConfigurer 已經配置過了,則直接返回,不然調用 apply 方法,這個 apply 方法最終會調用到 AbstractConfiguredSecurityBuilder#add 方法,將當前配置 configurer 收集起來。
HttpSecurity 中還有一個 addFilter 方法:
public HttpSecurity addFilter(Filter filter) { Class<? extends Filter> filterClass = filter.getClass(); if (!comparator.isRegistered(filterClass)) { throw new IllegalArgumentException( "The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."); } this.filters.add(filter); return this; }
這個 addFilter 方法的做用,主要是在各個 xxxConfigurer 進行配置的時候,會調用到這個方法,(xxxConfigurer 就是用來配置過濾器的),把 Filter 都添加到 fitlers 變量中。
最終在 HttpSecurity 的 performBuild 方法中,構建出來一個過濾器鏈:
@Override protected DefaultSecurityFilterChain performBuild() { filters.sort(comparator); return new DefaultSecurityFilterChain(requestMatcher, filters); }
先給過濾器排序,而後構造 DefaultSecurityFilterChain 對象。
好啦,這就是 HttpSecurity 的一個大體工做流程。把握住了這個工做流程,剩下的就只是一些簡單的重複的 xxxConfigurer 配置了,鬆哥就再也不囉嗦啦。
若是小夥伴們以爲有收穫,記得點個在看鼓勵下鬆哥哦~