您好,登錄后才能下訂單哦!
這篇文章主要講解了“spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解”吧!
我們先來看一張 WebSecurityConfigurerAdapter 的繼承關(guān)系圖:
在這層繼承關(guān)系中,有兩個(gè)非常重要的類:
WebSecurityConfigurer 其實(shí)是一個(gè)空接口,但是它里邊約束了一些泛型,如下:
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
SecurityConfigurer<Filter, T> {
}
這里邊的泛型很關(guān)鍵,這關(guān)乎到 WebSecurityConfigurer 的目的是啥!
同時(shí)這里還定義了新的泛型 T,T 需要繼承自 SecurityBuilder
所以 WebSecurityConfigurer 的目的我們可以理解為就是為了配置 WebSecurity。
我們來看下 WebSecurity 的定義:
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
}
沒錯(cuò),確實(shí)是這樣!WebSecurity 繼承自 AbstractConfiguredSecurityBuilder<Filter, WebSecurity> 同時(shí)實(shí)現(xiàn)了 SecurityBuilder
WebSecurity 的這些接口和繼承類。
AbstractConfiguredSecurityBuilder
首先 AbstractConfiguredSecurityBuilder 中定義了一個(gè)枚舉類,將整個(gè)構(gòu)建過程分為 5 種狀態(tài),也可以理解為構(gòu)建過程生命周期的五個(gè)階段,如下:
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;
}
}
五種狀態(tài)分別是 UNBUILT、INITIALIZING、CONFIGURING、BUILDING 以及 BUILT。另外還提供了兩個(gè)判斷方法,isInitializing 判斷是否正在初始化,isConfigured 表示是否已經(jīng)配置完畢。
AbstractConfiguredSecurityBuilder 中的方法比較多,松哥在這里列出來兩個(gè)關(guān)鍵的方法和大家分析:
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;
}
第一個(gè)就是這個(gè) add 方法,這相當(dāng)于是在收集所有的配置類。將所有的 xxxConfigure 收集起來存儲(chǔ)到 configurers 中,將來再統(tǒng)一初始化并配置,configurers 本身是一個(gè) LinkedHashMap ,key 是配置類的 class,value 是一個(gè)集合,集合里邊放著 xxxConfigure 配置類。當(dāng)需要對(duì)這些配置類進(jìn)行集中配置的時(shí)候,會(huì)通過 getConfigurers 方法獲取配置類,這個(gè)獲取過程就是把 LinkedHashMap 中的 value 拿出來,放到一個(gè)集合中返回。
另一個(gè)方法就是 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 類中,過濾器的構(gòu)建被轉(zhuǎn)移到 doBuild 方法上面了,不過在 AbstractSecurityBuilder 中只是定義了抽象的 doBuild 方法,具體的實(shí)現(xiàn)在 AbstractConfiguredSecurityBuilder。
doBuild 方法就是一邊更新狀態(tài),進(jìn)行進(jìn)行初始化。
beforeInit 是一個(gè)預(yù)留方法,沒有任何實(shí)現(xiàn)。
init 方法就是找到所有的 xxxConfigure,挨個(gè)調(diào)用其 init 方法進(jìn)行初始化。
beforeConfigure 是一個(gè)預(yù)留方法,沒有任何實(shí)現(xiàn)。
configure 方法就是找到所有的 xxxConfigure,挨個(gè)調(diào)用其 configure 方法進(jìn)行配置。
最后則是 performBuild 方法,是真正的過濾器鏈構(gòu)建方法,但是在 AbstractConfiguredSecurityBuilder 中 performBuild 方法只是一個(gè)抽象方法,具體的實(shí)現(xiàn)在它的子類中,也就是 WebSecurityConfigurer。
SecurityBuilder
SecurityBuilder 就是用來構(gòu)建過濾器鏈的,在 HttpSecurity 實(shí)現(xiàn) SecurityBuilder 時(shí),傳入的泛型就是 DefaultSecurityFilterChain,所以 SecurityBuilder#build 方法的功能很明確,就是用來構(gòu)建一個(gè)過濾器鏈出來,但是那個(gè)過濾器鏈?zhǔn)?Spring Security 中的。在 WebSecurityConfigurerAdapter 中定義的泛型是 SecurityBuilder
WebSecurity
WebSecurity 的核心邏輯集中在 performBuild 構(gòu)建方法上,我們一起來看下:
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
先來說一句,這里的 performBuild 方法只有一個(gè)功能,那就是構(gòu)建 FilterChainProxy。
把握住了這條主線,我們?cè)賮砜捶椒ǖ膶?shí)現(xiàn)就很容易了。
從這段分析中,我們可以看出來 WebSecurity 和 HttpSecurity 的區(qū)別:
這就是 WebSecurity 的主要作用,核心方法是 performBuild,其他方法都比較簡(jiǎn)單,就不一一解釋了。
最后我們?cè)賮砜?WebSecurityConfigurerAdapter,由于 WebSecurityConfigurer 只是一個(gè)空接口,WebSecurityConfigurerAdapter 就是針對(duì)這個(gè)空接口提供一個(gè)具體的實(shí)現(xiàn),最終目的還是為了方便你配置 WebSecurity。
WebSecurityConfigurerAdapter 中的方法比較多,但是根據(jù)我們前面的分析,提綱挈領(lǐng)的方法就兩個(gè),一個(gè)是 init,還有一個(gè) configure(WebSecurity web),其他方法都是為這兩個(gè)方法服務(wù)的。那我們就來看下這兩個(gè)方法:
先看 init 方法:
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
init 方法可以算是這里的入口方法了:首先調(diào)用 getHttp 方法進(jìn)行 HttpSecurity 的初始化。HttpSecurity 的初始化,實(shí)際上就是配置了一堆默認(rèn)的過濾器,配置完成后,最終還調(diào)用了 configure(http) 方法,該方法又配置了一些攔截器,不過在實(shí)際開發(fā)中,我們經(jīng)常會(huì)重寫 configure(http) 方法。HttpSecurity 配置完成后,再將 HttpSecurity 放入 WebSecurity 中,保存在 WebSecurity 的 securityFilterChainBuilders 集合里
configure(WebSecurity web) 方法實(shí)際上是一個(gè)空方法,我們?cè)趯?shí)際開發(fā)中可能會(huì)重寫該方法:
public void configure(WebSecurity web) throws Exception {
}
感謝各位的閱讀,以上就是“spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。