Spring Security 過濾器鏈(一)創建FilterChainProxy

一.簡介

官網官網文檔

參考:
深入淺出Spring Security(二):FilterChainProxy的創建過程
Spring Security 源碼分析(一):過濾器鏈
Spring Security源碼解析(二.創建FilterChainProxy)
Spring Security(六)—SpringSecurityFilterChain 加載流程深度解析

文中源碼均爲僞代碼,刪除了一些的細節

在這裏插入圖片描述

在SpringSecurity框架中,用戶想要訪問資源需要經過FilterChainProxy(本質上是個Filter,名字叫springSecurityFilterChain)來過濾。
裏面包含一組SecuriyFilterChains,每個uri都對應一個SecurityFilterChain,SecurityFilterChain包含多個SecurityFilter

二.FilterChainProxy的創建

虛線繼承/實現,如有錯亂以你爲準。
在這裏插入圖片描述
在這裏插入圖片描述
從圖中可以看出

  1. WebSecurityHttpSecurity都是Builder
    WebSecurity是SecurityBuilder< Filter >爲了創建FilterChainProxy
    HttpSecurity是SecurityBuilder< DefaultSecurityFilterChain >爲了創建 FilterChainProxy的filterChains。
  2. WebSecurityConfigurerAdapter是WebSecurity的Configurer
    WebSecurityConfiguration創建WebSecurity後,賦值給WebSecurity的配置configurers,
    並在WebSecurity要創建FilterChainProxy時,初始化,將HttpSecurity賦值給WebSecurity的securityFilterChainBuilders用於創建過濾器。

1.WebSecurityConfiguration

在這裏插入圖片描述

1.1 setFilterChainProxySecurityConfigurer()

public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
        this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
        Iterator var5;
        SecurityConfigurer config;
        for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
            config = (SecurityConfigurer)var5.next();
            Integer order = WebSecurityConfiguration.AnnotationAwareOrderComparator.lookupOrder(config);
            previousOrder = order;
        }

        var5 = webSecurityConfigurers.iterator();

        while(var5.hasNext()) {
            config = (SecurityConfigurer)var5.next();
            this.webSecurity.apply(config);
        }

        this.webSecurityConfigurers = webSecurityConfigurers;
}

該方法負責收集配置類對象列表webSecurityConfigurers,並創建WebSecurity,用於後面建造FilterChainProxy過濾器

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents. getWebSecurityConfigurers()}")
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers

@Value("#{}") 是SpEl表達式通常用來獲取bean的屬性或者調用bean的某個方法。
方法執行時,會先得到webSecurityConfigurers並排序(所有實現了WebSecurityConfigurerAdapter的配置類實例)

public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
        List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList();
        Map<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
        Iterator var3 = beansOfType.entrySet().iterator();

        while(var3.hasNext()) {
            Entry<String, WebSecurityConfigurer> entry = (Entry)var3.next();
            webSecurityConfigurers.add(entry.getValue());
        }

        return webSecurityConfigurers;
    }
  • 獲取類型爲WebSecurityConfigurer的bean

  • this.webSecurity.apply(config)WebSecurityConfigurerAdapter賦值給AbstractConfiguredSecurityBuilder(webSecurity的父類)的configurersAddedInInitializing 和 configurers 【2.1】。

1.2 springSecurityFilterChain()

@Bean( name = {"springSecurityFilterChain"})
public Filter springSecurityFilterChain() throws Exception {
    boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
    if (!hasConfigurers) {
        WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
        });
        this.webSecurity.apply(adapter);
    }

    return (Filter)this.webSecurity.build();
}

調用webSecurity.build(),建造出FilterChainProxy過濾器對象
並將其命名爲"springSecurityFilterChain"

webSecurity.build()–>AbstractSecurityBuilder.build()–>
AbstractConfiguredSecurityBuilder.doBuild()【2.2】

  • init(),configure()
    HttpSecurity添加到webSecurity的屬性securityFilterChainBuilders集合裏。
  • WebSecurity.performBuild()【3.1】
    securityFilterChainBuilders創建filterchains
    創建FilterChainProxy ;

2.AbstractConfiguredSecurityBuilder

WebSecurity繼承AbstractConfiguredSecurityBuilder
在這裏插入圖片描述
在這裏插入圖片描述

2.1 apply(C configurer),add(C configurer)

public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
        this.add(configurer);
        return configurer;
}
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
     Class<? extends SecurityConfigurer<O, B>> clazz = configurer.getClass();
     LinkedHashMap var3 = this.configurers;
     synchronized(this.configurers) {
          List<SecurityConfigurer<O, B>> configs = this.allowConfigurersOfSameType ? (List)this.configurers.get(clazz) : null;
          ((List)configs).add(configurer);
          
          this.configurers.put(clazz, configs);
          this.configurersAddedInInitializing.add(configurer);
     }
 }

2.2 doBuild()

protected final O doBuild() throws Exception {
        LinkedHashMap var1 = this.configurers;
        synchronized(this.configurers) {
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
            this.beforeInit();
            this.init();
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
            this.beforeConfigure();
            this.configure();
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
            O result = this.performBuild();
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT;
            return result;
        }
    }

2.3 init(),configure()

  • this.init() - >configurer.init(this) - > WebSecurityConfigurerAdapter.init(WebSecurity web) 【4.1】
  • this.configure(this)-> WebSecurityConfigurerAdapter.configure(WebSecurity web) 【4.2.2】;
private void init() throws Exception {
      Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
      Iterator var2 = configurers.iterator();
      SecurityConfigurer configurer;
      while(var2.hasNext()) {
          configurer = (SecurityConfigurer)var2.next();
          configurer.init(this);
      }
      var2 = this.configurersAddedInInitializing.iterator();

      while(var2.hasNext()) {
          configurer = (SecurityConfigurer)var2.next();
          configurer.init(this);
      }
  }
  private void configure() throws Exception {
        Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
        Iterator var2 = configurers.iterator();

        while(var2.hasNext()) {
            SecurityConfigurer<O, B> configurer = (SecurityConfigurer)var2.next();
            configurer.configure(this);
        }

    }

3. WebSecurity

在這裏插入圖片描述

3.1 performBuild()

protected Filter performBuild() throws Exception {
    // ...
    int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
    List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
    // 上文配置的需要忽略的請求
    for (RequestMatcher ignoredRequest : ignoredRequests) {
      securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
    }
    // 上文配置的 HttpSecurity 實例
    for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
      // 調用 build 方法生成 DefaultSecurityFilterChain
      securityFilterChains.add(securityFilterChainBuilder.build());
    }
    // 過濾器鏈代理
    FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

    // ...
  }

創建了FilterChainProxy並用securityFilterChainBuilder配置了FilterChainProxy的過濾器鏈securityFilterChains

4.WebSecurityConfigurerAdapter

在這裏插入圖片描述

4.1 init(),getHttp()

public void init(WebSecurity web) throws Exception {
     HttpSecurity http = this.getHttp();
     web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
         FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
         web.securityInterceptor(securityInterceptor);
     });
}
protected final HttpSecurity getHttp() throws Exception {
      AuthenticationManager authenticationManager = this.authenticationManager();
      this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
      this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
}

WebSecurityConfigurerApdater.init()方法,通過getHttp()方法獲得配置好的 HttpSecurity
然後 web.addSecurityFilterChainBuilder(http)HttpSecurity添加到webSecurity的屬性securityFilterChainBuilders集合裏。

public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
        this.securityFilterChainBuilders.add(securityFilterChainBuilder);
        return this;
    }

4.2 配置WebSecurityConfigurerAdapter

如果需要干預 spring security 配置,創建MyWebSecurityConfig 繼承WebSecurityConfigurerAdapter 並裝配到 Spring 容器中。比如

@Configuration
@EnableWebSecurity
@Slf4j
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
}

這裏我們可能會重寫WebSecurityConfigurerAdapter 中的綠框中的三個configure方法。

4.2.1 configure(HttpSecurity http)

原方法

    protected void configure(HttpSecurity http) throws Exception {
        this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
        ((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpBasic();
    }

例子:修改HttpSecurity,重新設置登錄,添加filter,修改攔截的路徑等

@Override
    protected void configure(HttpSecurity http) throws Exception {
            http
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login/submit")
                .failureUrl("/login_error").permitAll()
                .and()
                .authorizeRequests()
                    .antMatchers(HttpMethod.GET, "/login").permitAll()
                    .anyRequest().authenticated()
                .and()
                .csrf().disable();

        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

4.2.2 configure(WebSecurity web)

原方法

    public void configure(WebSecurity web) throws Exception {
    }

例子:修改WebSecurity 忽略靜態資源

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers(HttpMethod.GET,  "/*.html","/**/favicon.ico",
            "/**/*.html", "/**/*.css", "/**/*.*.woff", "/**/*.*.woff2", "/**/*.*.ttf","/**/*.js", "/**/*.png",
            "/**/*.jpg", "/**/*.ttf", "/**/*.map","/**/*.tpl","/**/*.map","/**/*.mp4", "/*.txt", "/**/*.txt",
            "/webjars/**","/csrf","/v2/api-docs",
            "/swagger-ui.html", "/swagger-resources/**");
}

4.2.3 configure(AuthenticationManagerBuilder auth)

原方法

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        this.disableLocalConfigureAuthenticationBldr = true;
    }

例子: 使用customUserService進行用戶信息的查詢。

@Override
protected void configure (AuthenticationManagerBuilder auth) throws Exception{
      auth.userDetailsService(customUserService()).passwordEncoder(passwordEncoder());
    }

5. HttpSecurity 16.2

參考
Spring Security : 配置 HttpSecurity 的 SecurityConfigurer
Spring Security原理篇(三) HttpSecurity

用於配置 web 請求的安全配置,默認會應用到所有請求
在上面的configure(HttpSecurity http)中。我們可以修改HttpSecurity 的一些設置。

5.1 默認的過濾器鏈

Spring Security中HttpSecurity是一個安全構建器SecurityBuilder,目的是構建一個對HTTP請求進行安全控制的DefaultSecurityFilterChain
對HttpSecurity進行配置,是通過一組安全配置器來完成的。這組安全配置器有一個共同的抽象基類AbstractHttpConfigurer。如下:

名稱 簡介
AnonymousConfigurer 配置匿名認證。 具體來講,是向HttpSecurity配置一對協調工作的AnonymousAuthenticationFilterAnonymousAuthenticationProvider,所有屬性採用合理的缺省值。這樣請求處理時,如果SecurityContextHolder中認證對象未設置(null的情況),則向其設置一個匿名認證對象AnonymousAuthenticationToken
ChannelSecurityConfigurer 配置ChannelProcessingFilter
CorsConfigurer 配置CorsFilter
CsrfConfigurer 配置CsrfFilter
DefaultLoginPageConfigurer 配置DefaultLoginPageGeneratingFilter/DefaultLogoutPageGeneratingFilter
ExceptionHandlingConfigurer 配置ExceptionTranslationFilter
ExpressionUrlAuthorizationConfigurer 配置FilterSecurityInterceptor
FormLoginConfigurer 配置UsernamePasswordAuthenticationFilter/DefaultLoginPageGeneratingFilter
HeadersConfigurer 配置HeaderWriterFilter
HttpBasicConfigurer 配置BasicAuthenticationFilter
JeeConfigurer 配置J2eePreAuthenticatedProcessingFilter
LogoutConfigurer 配置LogoutFilter
PortMapperConfigurer 配置共享對象PortMapper,用於HTTP/HTTPS兩種協議之間跳轉時的端口檢測。
RememberMeConfigurer 配置RememberMeAuthenticationFilter
RequestCacheConfigurer 配置RequestCacheAwareFilter
SecurityContextConfigurer 配置SecurityContextPersistenceFilter
ServletApiConfigurer 配置SecurityContextHolderAwareRequestFilter
SessionManagementConfigurer 配置SessionManagementFilter/ConcurrentSessionFilter
X509Configurer 配置X509AuthenticationFilter
OAuth2ClientConfigurer 配置OAuth2AuthorizationRequestRedirectFilter/OAuth2AuthorizationCodeGrantFilter;
配置共享對象:ClientRegistrationRepository
/OAuth2AuthorizedClientRepository
OAuth2LoginConfigurer 配置OAuth2AuthorizationRequestRedirectFilter/OAuth2LoginAuthenticationFilter;
配置共享對象:
ClientRegistrationRepository/
OAuth2AuthorizedClientRepository
/GrantedAuthoritiesMapper
OAuth2ResourceServerConfigurer 配置BearerTokenAuthenticationFilter;
配置共享對象:SessionCreationPolicy
OpenIDLoginConfigurer 配置OpenIDAuthenticationFilter;
配置共享對象:AuthenticationEntryPoint
/OpenIDAuthenticationProvider

比如 http.authorizeRequests()、http.formLogin()、http.httpBasic()分別創建了ExpressionUrlAuthorizationConfigurerFormLoginConfigurerHttpBasicConfigurer。在三個類從父級一直往上找,會發現它們都是SecurityConfigurer建造器的子類。
SecurityConfigurer中又有configure()方法。該方法被子類實現就用於創建各個過濾器,並將過濾器添加進HttpSecurity中維護的裝有Filter的List中

5.2 addFilter(Filter) 添加自定義的過濾器

在這裏插入圖片描述

http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

在過濾器鏈中UsernamePasswordAuthenticationFilter前插入我們自定義的authenticationTokenFilter

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章