springSecurity安全框架的學習和原理解讀


最近在公司的項目中使用了spring security框架,所以有機會來學習一下,公司的項目是使用springboot搭建 springBoot版本1.59

spring security 版本4.2.3

   (個人理解可能會有偏差,希望有不正確之處,大家能夠指出來,共同探討交流。)

目錄

一、Spring security框架簡介

 1、簡介

 2、框架原理

 3、框架的核心組件

二、自定義安全配置的加載機制

1、前提 基於自身業務需要

2、WebSecurityConfiguration類

3、AbstractSecurityBuilder類

4、舉例說明如何將一個Configurer轉換為filter

三、用戶登錄的驗證和授權過程


一、Spring security框架簡介

     1、簡介

           一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方式的安全框架(簡單說是對訪問權限進行控制嘛),應用的安全性包括用戶認證(Authentication)和用戶授權(Authorization)兩個部分。用戶認證指的是驗證某個用戶是否為系統中的合法主體,也就是說用戶能否訪問該系統。用戶認證一般要求用戶提供用戶名和密碼。系統通過校驗用戶名和密碼來完成認證過程。用戶授權指的是驗證某個用戶是否有權限執行某個操作。在一個系統中,不同用戶所具有的權限是不同的。比如對一個文件來說,有的用戶只能進行讀取,而有的用戶可以進行修改。一般來說,系統會為不同的用戶分配不同的角色,而每個角色則對應一系列的權限。   spring security的主要核心功能為 認證和授權,所有的架構也是基於這兩個核心功能去實現的。

     2、框架原理

     眾所周知 想要對對Web資源進行保護,最好的辦法莫過於Filter,要想對方法調用進行保護,最好的辦法莫過於AOP。所以springSecurity在我們進行用戶認證以及授予權限的時候,通過各種各樣的攔截器來控制權限的訪問,從而實現安全。
        如下為其主要過濾器  

  1.         WebAsyncManagerIntegrationFilter 
  2.         SecurityContextPersistenceFilter 
  3.         HeaderWriterFilter 
  4.         CorsFilter 
  5.         LogoutFilter
  6.         RequestCacheAwareFilter
  7.         SecurityContextHolderAwareRequestFilter
  8.         AnonymousAuthenticationFilter
  9.         SessionManagementFilter
  10.         ExceptionTranslationFilter
  11.         FilterSecurityInterceptor
  12.         UsernamePasswordAuthenticationFilter
  13.         BasicAuthenticationFilter

     3、框架的核心組件

  1.       SecurityContextHolder:提供對SecurityContext的訪問
  2.       SecurityContext,:持有Authentication對象和其他可能需要的信息
  3.       AuthenticationManager 其中可以包含多個AuthenticationProvider
  4.       ProviderManager對象為AuthenticationManager接口的實現類
  5.       AuthenticationProvider 主要用來進行認證操作的類 調用其中的authenticate()方法去進行認證操作
  6.       Authentication:Spring Security方式的認證主體
  7.       GrantedAuthority:對認證主題的應用層面的授權,含當前用戶的權限信息,通常使用角色表示
  8.      UserDetails:構建Authentication對象必須的信息,可以自定義,可能需要訪問DB得到
  9.       UserDetailsService:通過username構建UserDetails對象,通過loadUserByUsername根據userName獲取UserDetail對象 (可以在這里基於自身業務進行自定義的實現  如通過數據庫,xml,緩存獲取等)           

    
     

二、自定義安全配置的加載機制

    1、前提 基於自身業務需要

有關springSecrity安全框架的理解參考:springSecurity安全框架介紹

自定義了一個springSecurity安全框架的配置類 繼承WebSecurityConfigurerAdapter,重寫其中的方法configure,但是並不清楚自定義的類是如何被加載並起到作用,這里一步步通過debug來了解其中的加載原理。

其實在我們實現該類后,在web容器啟動的過程中該類實例對象會被WebSecurityConfiguration類處理。

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Autowired
    private CustAuthenticationProvider custAuthenticationProvider;

    // roles admin allow to access /admin/**
    // roles user allow to access /user/**
    // custom 403 access denied handler
    //重寫了其中的configure()方法設置了不同url的不同訪問權限
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/home", "/about","/img/*").permitAll()
                .antMatchers("/admin/**","/upload/**").hasAnyRole("ADMIN")
                .antMatchers("/order/**").hasAnyRole("USER","ADMIN")
                .antMatchers("/room/**").hasAnyRole("USER","ADMIN")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll()
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    }

    // create two users, admin and user
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

// auth.inMemoryAuthentication()
// .withUser("user").password("user").roles("USER")
// .and()
// .withUser("admin").password("admin").roles("ADMIN");

// auth.jdbcAuthentication()

        auth.authenticationProvider(custAuthenticationProvider);
    }

  2、WebSecurityConfiguration類

@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    private WebSecurity webSecurity;
    private Boolean debugEnabled;
    private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
    private ClassLoader beanClassLoader;
   
   ...省略部分代碼

    @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();
    }

  
    
    /*1、先執行該方法將我們自定義springSecurity配置實例 (可能還有系統默認的有關安全的配置實例 ) 配置實例中含有我們自定義業務的權限控制配置信息 放入到該對象的list數組中webSecurityConfigurers中 使用@Value注解來將實例對象作為形參注入 */   
 @Autowired(
        required = false
    )
    public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
    
    //創建一個webSecurity對象 
    this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
        if(this.debugEnabled != null) {
            this.webSecurity.debug(this.debugEnabled.booleanValue());
        }

        //對所有配置類的實例進行排序
        Collections.sort(webSecurityConfigurers, WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
        Integer previousOrder = null;
        Object previousConfig = null;


        //迭代所有配置類的實例 判斷其order必須唯一
        Iterator var5;
        SecurityConfigurer config;
        for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
            config = (SecurityConfigurer)var5.next();
            Integer order = Integer.valueOf(WebSecurityConfiguration.AnnotationAwareOrderComparator.lookupOrder(config));
            if(previousOrder != null && previousOrder.equals(order)) {
                throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
            }

            previousOrder = order;
        }


        //將所有的配置實例添加到創建的webSecutity對象中
        var5 = webSecurityConfigurers.iterator();

        while(var5.hasNext()) {
            config = (SecurityConfigurer)var5.next();
            this.webSecurity.apply(config);
        }
        //將webSercurityConfigures 實例放入該對象的webSecurityConfigurers屬性中
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

   
}

  2.1、 setFilterChainProxySecurityConfigurer()方法

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

   該參數webSecurityConfigurers會將所有的配置實例放入該形參中

 

該方法中 主要執行如下

     1、創建webSecurity對象

     2、主要檢驗了配置實例的order順序(order唯一 否則會報錯)

     3、將所有的配置實例存放進入到webSecurity對象中,其中配置實例中含有我們自定義業務的權限控制配置信息

 

2.2、springSecurityFilterChain()方法

   調用springSecurityFilterChain()方法,這個方法會判斷我們上一個方法中有沒有獲取到webSecurityConfigurers,沒有的話這邊會創建一個WebSecurityConfigurerAdapter實例,並追加到websecurity中。接着調用websecurity的build方法。實際調用的是websecurity的父類AbstractSecurityBuilder的build方法 ,最終返回一個名稱為springSecurityFilterChain的過濾器鏈。里面有眾多Filter(springSecurity其實就是依靠很多的Filter來攔截url從而實現權限的控制的安全框架)

3、AbstractSecurityBuilder類

public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
    private AtomicBoolean building = new AtomicBoolean();
    private O object;

  

    //調用build方法來返回過濾器鏈,還是調用SecurityBuilder的dobuild()方法

    public final O build() throws Exception {
        if(this.building.compareAndSet(false, true)) {
            this.object = this.doBuild();
            return this.object;
        } else {
            throw new AlreadyBuiltException("This object has already been built");
        }
    }

   //...省略部分代碼
}

  3.1 調用子類的doBuild()方法

public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
    private final Log logger;
    private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers;
    private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing;
    private final Map<Class<? extends Object>, Object> sharedObjects;
    private final boolean allowConfigurersOfSameType;
    private AbstractConfiguredSecurityBuilder.BuildState buildState;
    private ObjectPostProcessor<Object> objectPostProcessor;


    //doBuild()核心方法 init(),configure(),perFormBuild()
    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;
        }
    }

    protected abstract O performBuild() throws Exception;
    
    //調用init方法 調用配置類WebSecurityConfigurerAdapter的init()方法
    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);
        }

    }

    private Collection<SecurityConfigurer<O, B>> getConfigurers() {
        List<SecurityConfigurer<O, B>> result = new ArrayList();
        Iterator var2 = this.configurers.values().iterator();

        while(var2.hasNext()) {
            List<SecurityConfigurer<O, B>> configs = (List)var2.next();
            result.addAll(configs);
        }

        return result;
    }

    //...省略部分代碼
}

3.2 先調用本類的init()方法

build過程主要分三步,init->configure->peformBuild 

  • 1  init方法做了兩件事,一個就是調用getHttp()方法獲取一個http實例,並通過web.addSecurityFilterChainBuilder方法把獲取到的實例賦值給WebSecurity的securityFilterChainBuilders屬性,這個屬性在我們執行build的時候會用到,第二個就是為WebSecurity追加了一個postBuildAction,在build都完成后從http中拿出FilterSecurityInterceptor對象並賦值給WebSecurity。 
  • 2  getHttp()方法,這個方法在當我們使用默認配置時(大多數情況下)會為我們追加各種SecurityConfigurer的具體實現類到httpSecurity中,如exceptionHandling()方法會追加一個ExceptionHandlingConfigurer,sessionManagement()方法會追加一個SessionManagementConfigurer,securityContext()方法會追加一個SecurityContextConfigurer對象,這些SecurityConfigurer的具體實現類最終會為我們配置各種具體的filter。
  • 3 另外getHttp()方法的最后會調用configure(http),這個方法也是我們繼承WebSecurityConfigurerAdapter類后最可能會重寫的方法 。
  • 4 configure(HttpSecurity http)方法,默認的configure(HttpSecurity http)方法繼續向httpSecurity類中追加SecurityConfigurer的具體實現類,如authorizeRequests()方法追加一個ExpressionUrlAuthorizationConfigurer,formLogin()方法追加一個FormLoginConfigurer。 其中ExpressionUrlAuthorizationConfigurer這個實現類比較重要,因為他會給我們創建一個非常重要的對象FilterSecurityInterceptor對象,FormLoginConfigurer對象比較簡單,但是也會為我們提供一個在安全認證過程中經常用到會用的一個Filter:UsernamePasswordAuthenticationFilter。 

以上三個方法就是WebSecurityConfigurerAdapter類中init方法的主要邏輯,

public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {

    public void init(final WebSecurity web) throws Exception {
        final HttpSecurity http = this.getHttp();
        web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
            public void run() {
                FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
                web.securityInterceptor(securityInterceptor);
            }
        });
    }


 protected final HttpSecurity getHttp() throws Exception {
        if(this.http != null) {
            return this.http;
        } else {
            DefaultAuthenticationEventPublisher eventPublisher = (DefaultAuthenticationEventPublisher)this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
       

//添加認證的事件的發布者
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//獲取AuthenticationManager對象其中一至多個進行認證處理的對象實例,后面會進行講解 
AuthenticationManager authenticationManager = this.authenticationManager();
            this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
            Map<Class<? extends Object>, Object> sharedObjects = this.createSharedObjects();
            this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
            if(!this.disableDefaults) {
                ((HttpSecurity)((DefaultLoginPageConfigurer)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)this.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();
                ClassLoader classLoader = this.context.getClassLoader();
                List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
                Iterator var6 = defaultHttpConfigurers.iterator();

                while(var6.hasNext()) {
                    AbstractHttpConfigurer configurer = (AbstractHttpConfigurer)var6.next();
                    this.http.apply(configurer);
                }
            }

            //最終調用我們的繼承的WebSecurityConfigurerAdapter中重寫的configure()
            //將我們業務相關的權限配置規則信息進行初始化操作
            this.configure(this.http);
            return this.http;
        }
    }


 protected AuthenticationManager authenticationManager() throws Exception {
        if(!this.authenticationManagerInitialized) {
            this.configure(this.localConfigureAuthenticationBldr);
            if(this.disableLocalConfigureAuthenticationBldr) {
                this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
            } else {
                this.authenticationManager = (AuthenticationManager)this.localConfigureAuthenticationBldr.build();
            }

            this.authenticationManagerInitialized = true;
        }

        return this.authenticationManager;
    }



}

3.3、第二步configure

  • configure方法最終也調用到了WebSecurityConfigurerAdapter的configure(WebSecurity web)方法,默認實現中這個是一個空方法,具體應用中也經常重寫這個方法來實現特定需求。 

3.4、第三步 peformBuild

  • 具體的實現邏輯在WebSecurity類中 
  • 這個方法中最主要的任務就是遍歷securityFilterChainBuilders屬性中的SecurityBuilder對象,並調用他的build方法。 
    這個securityFilterChainBuilders屬性我們前面也有提到過,就是在WebSecurityConfigurerAdapter類的init方法中獲取http后賦值給了WebSecurity。因此這個地方就是調用httpSecurity的build方法。
  •  httpSecurity的build方式向其中追加一個個過濾器

public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements SecurityBuilder<Filter>, ApplicationContextAware {

...省略部分代碼

<span class="hljs-comment">//調用該方法通過securityFilterChainBuilder.build()方法來創建securityFilter過濾器</span>
<span class="hljs-comment">//並添加到securityFilterChains對象中,包裝成FilterChainProxy 返回</span>
<span class="hljs-function"><span class="hljs-keyword">protected</span> Filter <span class="hljs-title">performBuild</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{
    Assert.state(!<span class="hljs-keyword">this</span>.securityFilterChainBuilders.isEmpty(), <span class="hljs-string">"At least one SecurityBuilder&lt;? extends SecurityFilterChain&gt; needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "</span> + WebSecurity.class.getSimpleName() + <span class="hljs-string">".addSecurityFilterChainBuilder directly"</span>);
    <span class="hljs-keyword">int</span> chainSize = <span class="hljs-keyword">this</span>.ignoredRequests.size() + <span class="hljs-keyword">this</span>.securityFilterChainBuilders.size();
    List&lt;SecurityFilterChain&gt; securityFilterChains = <span class="hljs-keyword">new</span> ArrayList(chainSize);
    Iterator var3 = <span class="hljs-keyword">this</span>.ignoredRequests.iterator();

    <span class="hljs-keyword">while</span>(var3.hasNext()) {
        RequestMatcher ignoredRequest = (RequestMatcher)var3.next();
        securityFilterChains.add(<span class="hljs-keyword">new</span> DefaultSecurityFilterChain(ignoredRequest, <span class="hljs-keyword">new</span> Filter[<span class="hljs-number">0</span>]));
    }

    var3 = <span class="hljs-keyword">this</span>.securityFilterChainBuilders.iterator();

    <span class="hljs-keyword">while</span>(var3.hasNext()) {
        SecurityBuilder&lt;? extends SecurityFilterChain&gt; securityFilterChainBuilder = (SecurityBuilder)var3.next();
        securityFilterChains.add(securityFilterChainBuilder.build());
    }

    FilterChainProxy filterChainProxy = <span class="hljs-keyword">new</span> FilterChainProxy(securityFilterChains);
    <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.httpFirewall != <span class="hljs-keyword">null</span>) {
        filterChainProxy.setFirewall(<span class="hljs-keyword">this</span>.httpFirewall);
    }

    filterChainProxy.afterPropertiesSet();
    Filter result = filterChainProxy;
    <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.debugEnabled) {
        <span class="hljs-keyword">this</span>.logger.warn(<span class="hljs-string">"\n\n********************************************************************\n**********        Security debugging is enabled.       *************\n**********    This may include sensitive information.  *************\n**********      Do not use in a production system!     *************\n********************************************************************\n\n"</span>);
        result = <span class="hljs-keyword">new</span> DebugFilter(filterChainProxy);
    }

    <span class="hljs-keyword">this</span>.postBuildAction.run();
    <span class="hljs-keyword">return</span> (Filter)result;
}

}

 4、舉例說明如何將一個Configurer轉換為filter

ExpressionUrlAuthorizationConfigurer的繼承關系 
ExpressionUrlAuthorizationConfigurer->AbstractInterceptUrlConfigurer->AbstractHttpConfigurer->SecurityConfigurerAdapter->SecurityConfigurer 
對應的init方法在SecurityConfigurerAdapter類中,是個空實現,什么也沒有做,configure方法在SecurityConfigurerAdapter類中也有一個空實現,在AbstractInterceptUrlConfigurer類中進行了重寫 

Abstractintercepturlconfigurer.java代碼 

@Override  
    public void configure(H http) throws Exception {  
        FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);  
        if (metadataSource == null) {  
            return;  
        }  
        FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(  
                http, metadataSource, http.getSharedObject(AuthenticationManager.class));  
        if (filterSecurityInterceptorOncePerRequest != null) {  
            securityInterceptor  
                    .setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);  
        }  
        securityInterceptor = postProcess(securityInterceptor);  
        http.addFilter(securityInterceptor);  
        http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);  
    }  
...  
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {  
        AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));  
        return postProcess(result);  
    }  
...  
private FilterSecurityInterceptor createFilterSecurityInterceptor(H http,               FilterInvocationSecurityMetadataSource metadataSource,               AuthenticationManager authenticationManager) throws Exception {  
        FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();  
        securityInterceptor.setSecurityMetadataSource(metadataSource);  
        securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));  
        securityInterceptor.setAuthenticationManager(authenticationManager);  
        securityInterceptor.afterPropertiesSet();  
        return securityInterceptor;  
    }  

4.1、 在這個類的configure中創建了一個FilterSecurityInterceptor,並且也可以明確看到spring security默認給我們創建的AccessDecisionManager是AffirmativeBased。 

4.2、.最后再看下HttpSecurity類執行build的最后一步 performBuild,這個方法就是在HttpSecurity中實現的 

Httpsecurity.java代碼 

@Override  
    protected DefaultSecurityFilterChain performBuild() throws Exception {  
        Collections.sort(filters, comparator);  
        return new DefaultSecurityFilterChain(requestMatcher, filters);  
    }  


可以看到,這個類只是把我們追加到HttpSecurity中的security進行了排序,用的排序類是FilterComparator,從而保證我們的filter按照正確的順序執行。接着將filters構建成filterChian返回。在前面WebSecurity的performBuild方法中,這個返回值會被包裝成FilterChainProxy,並作為WebSecurity的build方法的放回值。從而以springSecurityFilterChain這個名稱注冊到springContext中(在WebSecurityConfiguration中做的) 

4.3.在WebSecurity的performBuild方法的最后一步還執行了一個postBuildAction.run,這個方法也是spring security給我們提供的一個hooks,可以在build完成后再做一些事情,比如我們在WebSecurityConfigurerAdapter類的init方法中我們利用這個hook在構建完成后將FilterSecurityInterceptor賦值給了webSecurity類的filterSecurityInterceptor屬性

 

三、用戶登錄的驗證和授權過程

      1、用戶一次完整的登錄驗證和授權,是一個請求經過 層層攔截器從而實現權限控制,整個web端配置為DelegatingFilterProxy(springSecurity的委托過濾其代理類 ),它並不實現真正的過濾,而是所有過濾器鏈的代理類,真正執行攔截處理的是由spring 容器管理的個個filter bean組成的filterChain.

調用實際的FilterChainProxy 的doFilterInternal()方法 去獲取所有的攔截器並進行過濾處理如下是DelegatingFilterProxy的doFilter()方法

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Filter delegateToUse = this.delegate;
        if(delegateToUse == null) {
            Object var5 = this.delegateMonitor;
            synchronized(this.delegateMonitor) {
                delegateToUse = this.delegate;
                if(delegateToUse == null) {
                    WebApplicationContext wac = this.findWebApplicationContext();
                    if(wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                    }

                    delegateToUse = this.initDelegate(wac);
                }

                this.delegate = delegateToUse;
            }
        }

//調用實際的FilterChainProxy 的doFilterInternal()方法 去獲取所有的攔截器並進行過濾處理
        this.invokeDelegate(delegateToUse, request, response, filterChain);
    }

調用實際的FilterChainProxy 的doFilter()方法 去獲取所有的攔截器並進行過濾處理。

2、FilterChainProxy類

    最終調用FilterChainProxy 的doFilterInternal()方法,獲取所有的過濾器實例

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
        if(clearContext) {
            try {
                request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
                //doFilter 調用doFilterInternal方法
                this.doFilterInternal(request, response, chain);
            } finally {
                SecurityContextHolder.clearContext();
                request.removeAttribute(FILTER_APPLIED);
            }
        } else {
            this.doFilterInternal(request, response, chain);
        }

    }

    private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
        HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
         //過去所有的過濾器
        List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
        if(filters != null && filters.size() != 0) {
            FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
            vfc.doFilter(fwRequest, fwResponse);
        } else {
            if(logger.isDebugEnabled()) {
                logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null?" has no matching filters":" has an empty filter list"));
            }

            fwRequest.reset();
            chain.doFilter(fwRequest, fwResponse);
        }
    }


  private List<Filter> getFilters(HttpServletRequest request) {
       //遍歷所有的matcher類 如果支持就繼續獲取
        Iterator var2 = this.filterChains.iterator();

        SecurityFilterChain chain;
        do {
            if(!var2.hasNext()) {
                return null;
            }

            chain = (SecurityFilterChain)var2.next();
        } while(!chain.matches(request));
        //后去匹配中的所有過濾器
        return chain.getFilters();
    }

如上 其實是獲取到本次請求的所有filter 並安裝指定順序進行執行doFilter()方法

這是筆者本次業務請求所要執行的所有過濾器 

  1.     WebAsyncManagerIntegrationFilter
  2.      SecurityContextPersistenceFilter
  3.      HeaderWriterFilter     
  4.      LogoutFilter
  5.      UsernamePasswordAuthenticationFilter
  6.      RequestCacheAwareFilter
  7.      SecurityContextHolderAwareRequestFilter
  8.      AnonymousAuthenticationFilter
  9.      SessionManagementFilter
  10.      ExceptionTranslationFilter
  11.      FilterSecurityInterceptor

關於springSecutity攔截器的介紹請參考如下鏈接地址

https://blog.csdn.net/dushiwodecuo/article/details/78913113

http://blog.didispace.com/xjf-spring-security-4/

https://www.cnblogs.com/HHR-SUN/p/7095720.html

https://blog.csdn.net/zheng963/article/details/50427320

https://blog.csdn.net/m0_37834471/article/details/81142246

https://www.cnblogs.com/mingluosunshan/p/5485259.html

 

原文地址:https://blog.csdn.net/liushangzaibeijing/article/details/81220610


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM