自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Security非常難的地方就是這個(gè)了

開發(fā) 架構(gòu)
為什么要這么復(fù)雜?我第一次看到HttpSecurity的結(jié)構(gòu)時(shí)我懷疑我自己是不是Java開發(fā)。多年以后,當(dāng)我深入學(xué)習(xí)了之后才理解了這種設(shè)計(jì)。作為一個(gè)框架,尤其是安全框架,配置必須足夠靈活才能適用于更多的業(yè)務(wù)場(chǎng)景。

[[442392]]

Spring Security最難的地方就是HttpSecurity的頂層設(shè)計(jì)。不信你看看HttpSecurity的定義。

  1. public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity> 
  2.         implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> { 
  3.     // 省略 

感覺(jué)不到的話,再給你看看UML圖:

為什么要這么復(fù)雜?我第一次看到HttpSecurity的結(jié)構(gòu)時(shí)我懷疑我自己是不是Java開發(fā)。多年以后,當(dāng)我深入學(xué)習(xí)了之后才理解了這種設(shè)計(jì)。作為一個(gè)框架,尤其是安全框架,配置必須足夠靈活才能適用于更多的業(yè)務(wù)場(chǎng)景。Spring Security采取了配置與構(gòu)建分離的架構(gòu)設(shè)計(jì)來(lái)保證這一點(diǎn)。

配置與構(gòu)建分離

配置只需要去收集配置項(xiàng),構(gòu)建只需要把所有的配置構(gòu)建成目標(biāo)對(duì)象。各干各的,分離職責(zé),這種做法能夠提高代碼的可維護(hù)性和可讀寫性。Spring Security利用接口隔離把配置和構(gòu)建進(jìn)行高度抽象,提高靈活度,降低復(fù)雜度。不過(guò)這個(gè)體系依然非常龐大。為了降低學(xué)習(xí)難度需要把大問(wèn)題拆解成小問(wèn)題,各個(gè)擊破,這種學(xué)習(xí)方法在學(xué)習(xí)一些復(fù)雜的抽象理論時(shí)很湊效。

SecurityBuilder

SecurityBuilder就是對(duì)構(gòu)建的抽象。你看上面的類圖過(guò)于復(fù)雜,而看SecurityBuilder就非常的簡(jiǎn)單了。

  1. public interface SecurityBuilder<O> { 
  2.     // 構(gòu)建 
  3.  O build() throws Exception; 

就一個(gè)動(dòng)作,構(gòu)建泛化的目標(biāo)對(duì)象O。通過(guò)下面這一組抽象和具體的定義我想你應(yīng)該明白SecurityBuilder了吧。

  1. // 抽象 
  2. SecurityBuilder -> O 
  3. // 具體 
  4. HttpSecurity->DefaultSecurityFilterChain 

一句話,構(gòu)建的活都是我來(lái)干。

  1. public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> { 
  2.  
  3.  private AtomicBoolean building = new AtomicBoolean(); 
  4.  
  5.  private O object; 
  6.  
  7.  @Override 
  8.  public final O build() throws Exception { 
  9.   if (this.building.compareAndSet(falsetrue)) { 
  10.       //構(gòu)建的核心邏輯由鉤子方法提供 
  11.    this.object = doBuild(); 
  12.    return this.object; 
  13.   } 
  14.   throw new AlreadyBuiltException("This object has already been built"); 
  15.  } 
  16.      // 獲取構(gòu)建目標(biāo)對(duì)象 
  17.  public final O getObject() { 
  18.   if (!this.building.get()) { 
  19.    throw new IllegalStateException("This object has not been built"); 
  20.   } 
  21.   return this.object; 
  22.  } 
  23.  
  24.  /** 
  25.   *  鉤子方法 
  26.   */ 
  27.  protected abstract O doBuild() throws Exception; 
  28.  

它通過(guò)原子類AtomicBoolean對(duì)構(gòu)建方法build()進(jìn)行了調(diào)用限制:每個(gè)目標(biāo)對(duì)象只能被構(gòu)建一次,避免安全策略發(fā)生不一致的情況。構(gòu)建方法還加了final關(guān)鍵字,不可覆寫!構(gòu)建的核心邏輯通過(guò)預(yù)留的鉤子方法doBuild()來(lái)擴(kuò)展,鉤子方法是很常見的一種繼承策略。另外AbstractSecurityBuilder還提供了獲取已構(gòu)建目標(biāo)對(duì)象的方法getObject。

一句話,構(gòu)建的活我只干一次。

HttpSecurityBuilder

  1. public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> 
  2.   extends SecurityBuilder<DefaultSecurityFilterChain> { 
  3.  
  4.      // 根據(jù)類名獲取配置   
  5.  <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz); 
  6.     // 根據(jù)類名移除配置  
  7.  <C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz); 
  8.     // 把某個(gè)對(duì)象設(shè)置為共享,以便于在多個(gè)SecurityConfigurer中使用 
  9.  <C> void setSharedObject(Class<C> sharedType, C object); 
  10.     // 獲取某個(gè)共享對(duì)象 
  11.  <C> C getSharedObject(Class<C> sharedType); 
  12.     //  添加額外的 AuthenticationProvider 
  13.  H authenticationProvider(AuthenticationProvider authenticationProvider); 
  14.     //  添加額外的 UserDetailsService 
  15.  H userDetailsService(UserDetailsService userDetailsService) throws Exception; 
  16.     // 在過(guò)濾器鏈已有的afterFilter類后面注冊(cè)一個(gè)過(guò)濾器 
  17.  H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter); 
  18.     // 在過(guò)濾器鏈已有的beforeFilter類前面注冊(cè)一個(gè)過(guò)濾器 
  19.  H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter); 
  20.     // 在過(guò)濾器鏈注冊(cè)一個(gè)過(guò)濾器,該過(guò)濾器必須在內(nèi)置注冊(cè)表 FilterOrderRegistration 中 
  21.  H addFilter(Filter filter); 
  22.  

HttpSecurityBuilder對(duì)DefaultSecurityFilterChain的構(gòu)建進(jìn)行了增強(qiáng),為其構(gòu)建器增加了一些額外的獲取配置或管理配置的入口,參見上面的注釋。補(bǔ)充一點(diǎn)這個(gè)接口最大的功能就是打通了構(gòu)建和配置的關(guān)系,可以操作下面要講的SecurityConfigurer。

一句話,我只構(gòu)建DefaultSecurityFilterChain。

SecurityConfigurer

SecurityConfigurer是對(duì)配置的抽象。配置只是手段,構(gòu)建才是目的。因此配置是對(duì)構(gòu)建的配置。

  1. public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> { 
  2.    // 構(gòu)建器初始化需要注入的配置,用來(lái)后續(xù)的信息共享 
  3.  void init(B builder) throws Exception; 
  4.    // 其它的一些必要配置   
  5.  void configure(B builder) throws Exception; 

SecurityConfigurer有兩個(gè)方法,都非常重要。一個(gè)是init方法,這個(gè)方法你可以認(rèn)為是SecurityBuilder構(gòu)造函數(shù)的邏輯。如果你想在SecurityBuilder初始化的時(shí)候執(zhí)行一些邏輯或者在后續(xù)配置中共享一些變量的話就可以在init方法中去實(shí)現(xiàn);第二個(gè)方法是configure,為SecurityBuilder配置一些必要的屬性。到這里還沒(méi)完?這兩個(gè)方法有著明確的先后執(zhí)行順序。在一次構(gòu)建內(nèi)可能有多個(gè)SecurityConfigurer,只有全部的init逐個(gè)執(zhí)行完畢后才會(huì)逐個(gè)執(zhí)行configure方法。相關(guān)的源碼在AbstractConfiguredSecurityBuilder中的標(biāo)記部分:

  1.  @Override 
  2. protected final O doBuild() throws Exception { 
  3.  synchronized (this.configurers) { 
  4.   this.buildState = BuildState.INITIALIZING; 
  5.   beforeInit(); 
  6.            // ① 執(zhí)行所有的初始化方法 
  7.   init(); 
  8.   this.buildState = BuildState.CONFIGURING; 
  9.   beforeConfigure(); 
  10.            // ② 執(zhí)行所有的configure方法 
  11.   configure(); 
  12.   this.buildState = BuildState.BUILDING; 
  13.   O result = performBuild(); 
  14.   this.buildState = BuildState.BUILT; 
  15.   return result; 
  16.  } 

一句話,配置SecurityBuilder的事都是我來(lái)干。

SecurityConfigurerAdapter

SecurityConfigurer在某些場(chǎng)景下是有局限性的,它不能獲取正在配置的SecurityBuilder,因此你無(wú)法進(jìn)一步操作SecurityBuilder,配置的擴(kuò)展性將大打折扣。因此引入了SecurityConfigurerAdapter來(lái)擴(kuò)展SecurityConfigurer。

  1. public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> { 
  2.  
  3.     private B securityBuilder; 
  4.  
  5.     private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor(); 
  6.  
  7.     @Override 
  8.     public void init(B builder) throws Exception { 
  9.     } 
  10.  
  11.     @Override 
  12.     public void configure(B builder) throws Exception { 
  13.     } 
  14.    // 獲取正在配置的構(gòu)建器,以暴露構(gòu)建器的api 
  15.     public B and() { 
  16.         return getBuilder(); 
  17.     } 
  18.   
  19.     protected final B getBuilder() { 
  20.         Assert.state(this.securityBuilder != null"securityBuilder cannot be null"); 
  21.         return this.securityBuilder; 
  22.     } 
  23.      
  24.     //  用復(fù)合對(duì)象后置處理器去處理對(duì)象,以改變一些對(duì)象的特性 
  25.     @SuppressWarnings("unchecked"
  26.     protected <T> T postProcess(T object) { 
  27.         return (T) this.objectPostProcessor.postProcess(object); 
  28.     } 
  29.     // 添加一個(gè)ObjectPostProcessor到符合構(gòu)建器 
  30.     public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) { 
  31.         this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor); 
  32.     } 
  33.     // 設(shè)置 需要配置的構(gòu)建器,這樣可以讓多個(gè)SecurityConfigurerAdapter去配置一個(gè)SecurityBuilder 
  34.     public void setBuilder(B builder) { 
  35.         this.securityBuilder = builder; 
  36.     } 
  37.     // 其它省略 

這樣可以指定SecurityBuilder,而且可以把SecurityBuilder暴露出來(lái),隨時(shí)隨地去調(diào)整SecurityBuilder,靈活性大大提高。

具體說(shuō)的話,你可以通過(guò)and()方法獲取SecurityBuilder并對(duì)SecurityBuilder的其它配置項(xiàng)進(jìn)行操作,比如上圖中SecurityConfigurerAdapter之間的切換。除此之外還引入了ObjectPostProcessor來(lái)后置操作一些并不開放的內(nèi)置對(duì)象。關(guān)于ObjectPostProcessor會(huì)找個(gè)合適的場(chǎng)景去講解它。

一句話,配置SecurityBuilder不算什么,靈活適配才是花活。

AbstractHttpConfigurer

不是所有的配置都是有用的,有些配置我們希望有個(gè)關(guān)閉的入口功能。比如csrf功能,控制著csrf的配置的是CsrfConfigurer,如果CsrfConfigurer有一個(gè)關(guān)閉功能就好了。因此從SecurityConfigurerAdapter衍生出AbstractHttpConfigurer來(lái)滿足這個(gè)需求。

  1.   public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>> 
  2.         extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> { 
  3.     // 關(guān)閉當(dāng)前配置 
  4.     @SuppressWarnings("unchecked"
  5.     public B disable() { 
  6.         getBuilder().removeConfigurer(getClass()); 
  7.         return getBuilder(); 
  8.     } 
  9.     //  增強(qiáng)了父類的新增ObjectPostProcessor方法  
  10.     @SuppressWarnings("unchecked"
  11.     public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) { 
  12.         addObjectPostProcessor(objectPostProcessor); 
  13.         return (T) this; 
  14.     } 
  15.  

AbstractHttpConfigurer的實(shí)現(xiàn)類非常多,日常的配置項(xiàng)大都由AbstractHttpConfigurer的實(shí)現(xiàn)類來(lái)控制。

這個(gè)類是做定制化配置的一個(gè)重要入口之一,如果你想精通Spring Security,這個(gè)類一定要掌握。

一句話,我能“殺”我自己。

AbstractConfiguredSecurityBuilder

我們希望有多個(gè)SecurityConfigurer配置SecurityBuilder,表單登錄的、會(huì)話管理、csrf等等。用到什么配置什么,讓配置基于策略。因此引入了AbstractConfiguredSecurityBuilder。

  1. public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { 
  2.        // 把 objectPostProcessor注入到configurer 
  3.  configurer.addObjectPostProcessor(this.objectPostProcessor); 
  4.        // 為 SecurityConfigurerAdapter 設(shè)置Builder 以便于能夠get到    
  5.        // 注意區(qū)別于其它SecurityConfigurer 
  6.  configurer.setBuilder((B) this); 
  7.  add(configurer); 
  8.  return configurer; 
  9.  
  10. public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { 
  11.  add(configurer); 
  12.  return configurer; 

通過(guò)上面兩個(gè)apply方法就可以把所有的SecurityConfigurer適配進(jìn)來(lái),然后通過(guò)doBuilder進(jìn)行精細(xì)化構(gòu)建生命周期。你可以在各個(gè)生命周期階段進(jìn)行一些必要的操作。

一句話,所有的配置都由我來(lái)進(jìn)行適配。

總結(jié)

我們把Spring Security整個(gè)配置構(gòu)建體系拆分了來(lái)看會(huì)簡(jiǎn)單的多一些。即使這樣想理解這個(gè)體系也絕非靠一篇兩篇文章也是不現(xiàn)實(shí)的。不過(guò)從中也可以看得出一個(gè)道理,如果你的代碼想高度靈活,就必須把各個(gè)生命周期分層地高度抽象才行。

本文轉(zhuǎn)載自微信公眾號(hào)「碼農(nóng)小胖哥」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系碼農(nóng)小胖哥公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)小胖哥
相關(guān)推薦

2021-07-06 07:21:16

Spring 安全平臺(tái)

2024-02-26 08:21:51

CPUkafka死循環(huán)

2021-04-23 07:33:10

SpringSecurity單元

2024-06-03 09:04:30

2012-04-11 11:22:33

iPhone

2021-08-29 18:36:57

項(xiàng)目

2023-04-10 11:41:15

2023-12-10 13:58:17

2009-06-18 14:18:23

Spring secu

2022-01-26 00:05:00

接口Spring管理器

2022-02-08 15:29:27

故障運(yùn)維服務(wù)器

2023-12-27 14:04:00

Spring框架參數(shù)

2022-10-11 14:58:00

性能優(yōu)化Java

2024-11-08 14:11:09

2024-02-04 09:19:00

Nacos動(dòng)態(tài)化線程池

2022-05-19 11:29:14

計(jì)時(shí)攻擊SpringSecurity

2023-12-08 12:12:21

2022-11-26 00:00:02

2020-06-05 08:43:28

微信地?cái)?/a>微信支付分

2022-08-30 08:50:07

Spring權(quán)限控制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)