溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解

發(fā)布時(shí)間:2021-12-07 13:48:37 來源:億速云 閱讀:243 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解”吧!

我們先來看一張 WebSecurityConfigurerAdapter 的繼承關(guān)系圖:

spring boot中的WebSecurityConfigurerAdapter繼承關(guān)系怎么理解  

在這層繼承關(guān)系中,有兩個(gè)非常重要的類:

  • SecurityBuilder
  • SecurityConfigurer

1.WebSecurityConfigurer

WebSecurityConfigurer 其實(shí)是一個(gè)空接口,但是它里邊約束了一些泛型,如下:

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
  SecurityConfigurer<Filter, T> {

}
 

這里邊的泛型很關(guān)鍵,這關(guān)乎到 WebSecurityConfigurer 的目的是啥!

  1. SecurityBuilder 中的泛型 Filter,表示 SecurityBuilder 最終的目的是為了構(gòu)建一個(gè) Filter 對(duì)象出來。
  2. SecurityConfigurer 中兩個(gè)泛型,第一個(gè)表示的含義也是 SecurityBuilder 最終構(gòu)建的對(duì)象。

同時(shí)這里還定義了新的泛型 T,T 需要繼承自 SecurityBuilder,根據(jù) WebSecurityConfigurerAdapter 中的定義,我們可以知道,T 就是 WebSecurity,我們也大概能猜出 WebSecurity 就是 SecurityBuilder的子類。

所以 WebSecurityConfigurer 的目的我們可以理解為就是為了配置 WebSecurity。

 

2.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,所以最終構(gòu)建的是一個(gè)普通 Filter,其實(shí)就是 FilterChainProxy。

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)就很容易了。

  1. 首先統(tǒng)計(jì)過濾器鏈的總條數(shù),總條數(shù)包括兩個(gè)方面,一個(gè)是 ignoredRequests,這是忽略的請(qǐng)求,通過 WebSecurity 配置的忽略請(qǐng)求,另一個(gè)則是 securityFilterChainBuilders,也就是我們通過 HttpSecurity 配置的過濾器鏈,有幾個(gè)就算幾個(gè)。
  2. 創(chuàng)建 securityFilterChains 集合,并且遍歷上面提到的兩種類型的過濾器鏈,并將過濾器鏈放入 securityFilterChains 集合中。
  3. HttpSecurity 構(gòu)建出來的過濾器鏈對(duì)象就是 DefaultSecurityFilterChain,所以可以直接將 build 結(jié)果放入 securityFilterChains 中,而 ignoredRequests 中保存的則需要重構(gòu)一下才可以存入 securityFilterChains。
  4. securityFilterChains 中有數(shù)據(jù)之后,接下來創(chuàng)建一個(gè) FilterChainProxy。
  5. 給新建的 FilterChainProxy 配置上防火墻。
  6. 最后我們返回的就是 FilterChainProxy 的實(shí)例。

從這段分析中,我們可以看出來 WebSecurity 和 HttpSecurity 的區(qū)別:

  1. HttpSecurity 目的是構(gòu)建過濾器鏈,一個(gè) HttpSecurity 對(duì)象構(gòu)建一條過濾器鏈,一個(gè)過濾器鏈中有 N 個(gè)過濾器,HttpSecurity 所做的事情實(shí)際上就是在配置這 N 個(gè)過濾器。
  2. WebSecurity 目的是構(gòu)建 FilterChainProxy,一個(gè) FilterChainProxy 中包含有多個(gè)過濾器鏈和一個(gè) Firewall。

這就是 WebSecurity 的主要作用,核心方法是 performBuild,其他方法都比較簡(jiǎn)單,就不一一解釋了。

 

3.WebSecurityConfigurerAdapter

最后我們?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)注!

向AI問一下細(xì)節(jié)

免責(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)容。

AI