spring-security源碼-初始化(九)


說明

使用spring-boot 我們引入security的包 就可以自動實現簡單的登錄,是怎么做到的呢?

知道spring-security源碼,我們的可以通過打斷點方式,找到各個核心源碼處,知道各個配置原理,和擴展點 完成業務定制化邏輯

 

security自動化配置

1.在spring-boot-autoconfigure的spring.factories引入了security的自動化配置。我們主要看最核心的org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

spriing自動化配置原理可以參考《Spring Boot-Starter(九)》

 

 

2.SecurityAutoConfiguration實現

Import導入原理可以參考《spring源碼閱讀(五)-Spring Import注解使用》《Spring源碼閱讀(六)-ConfigurationClassPostProcessor》

@Configuration(
        proxyBeanMethods = false
)
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})//class path有此類加載
@EnableConfigurationProperties({SecurityProperties.class})
//Import導入 我們主要看WebSecurityEnablerConfiguration
@Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
    public SecurityAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean({AuthenticationEventPublisher.class})//容器沒有這個bean觸發加載
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }
}

3.我們繼續看EnableWebSecurity

@Configuration(
        proxyBeanMethods = false//不需要代理
)
@ConditionalOnBean({WebSecurityConfigurerAdapter.class})//容器中有WebSecurityConfigurerAdapter對象觸發自動加載
@ConditionalOnMissingBean(//容器中不能出現springSecurityFilterChain的實例
        name = {"springSecurityFilterChain"}
)
@ConditionalOnWebApplication(
        type = ConditionalOnWebApplication.Type.SERVLET
)
@EnableWebSecurity//組合注解
public class WebSecurityEnablerConfiguration {
    public WebSecurityEnablerConfiguration() {
    }
}

4.

這里又用到了Import導入 我們主要看WebSecurityConfiguration

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
        HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

    /**
     * Controls debugging support for Spring Security. Default is false.
     * @return if true, enables debug support with Spring Security
     */
    boolean debug() default false;

}

5.WebSecurityConfiguration首先會初始化一個webSecurity管理我們定義的WebSecurityConfigurerAdapter子類

org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#setFilterChainProxySecurityConfigurer

  /**
     *autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()
     * 這個類里面就是根據beanFactory獲取WebSecurityConfigurer的實現 也就是我們的配置的WebSecurityConfigurerAdapter子類
     */
    @Autowired(required = false)
    public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
                                                      @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
            throws Exception {
        //這里是通過new創建WebSecurity  同時通過objectPostProcessor 從容器中找如果有的話就依賴注入
        //我們可以閱讀里面成員變量原理 通過容器注入對應對象完成初始化復制
        this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
        if (this.debugEnabled != null) {
            this.webSecurity.debug(this.debugEnabled);
        }
        //排序
        webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
        Integer previousOrder = null;
        Object previousConfig = null;
        for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
            Integer order = 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;
            previousConfig = config;
        }
        //循環遍歷設置到 add 到webSecurity成員變量configurers webSecurityConfigures為我們自定義繼承的WebSecurityConfigureAdapter配置類
        for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
            this.webSecurity.apply(webSecurityConfigurer);
        }
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

6.

public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain"; 最后通過org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration 注入到Servlet

org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain

 @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
        boolean hasFilterChain = !this.securityFilterChains.isEmpty();
        Assert.state(!(hasConfigurers && hasFilterChain),
                "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
        //我們沒有配置 這里應該是配置一個默認的
        if (!hasConfigurers && !hasFilterChain) {
            WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
                    .postProcess(new WebSecurityConfigurerAdapter() {
                    });
            this.webSecurity.apply(adapter);
        }

        /**
         * securityFilterChains 是通過容器獲取 通過@Autowired set方法注入
         * 這里也是一個擴展點 我們可以增加手動增加securityFilterChain
         */
        for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
            this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
            for (Filter filter : securityFilterChain.getFilters()) {
                //如果這個filter是FilterSecurityInterceptor 則加入到securityInterceptor
                if (filter instanceof FilterSecurityInterceptor) {
                    this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
                    break;
                }
            }
        }
        /**
         * webSecurityCustomizers也是從容器獲取
         * 也是一個擴展點。我們可以自定義 在build前對webSecurity做一些定制化操作
         * @通過@Autowired set方法注入
         */
        for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
            customizer.customize(this.webSecurity);
        }
        /*
        *<1>執行build
         */
        return this.webSecurity.build();
    }

<1>

模板模式

org.springframework.security.config.annotation.AbstractSecurityBuilder#build

    @Override
    public final O build() throws Exception {
        if (this.building.compareAndSet(false, true)) {
            //<2>模板模式
            this.object = doBuild();
            return this.object;
        }
        throw new AlreadyBuiltException("This object has already been built");
    }

<2>

所有配置都繼承這個類

org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild

 /**
     * @return
     * @throws Exception
     */
    @Override
    protected final O doBuild() throws Exception {
        synchronized (this.configurers) {
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
            //空實現
            beforeInit();
            //<3>初始化config
            init();
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
            beforeConfigure();
            //<4>             configure();
            this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
//模板方法 抽象的子類必須實現 真正的build方法 O result
= performBuild(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT; return result; } }

針對不同的build會調用不同的performBuild方法

如webSecurity則是調用<10>

如HttpSecurity則調用<13>

針對DefaultPasswordEncoderAuthenticationManagerBuilder 則調用<16>

<3>

org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#init

@SuppressWarnings("unchecked")
    private void init() throws Exception {
        //獲得configures調用configures的init 注意不同的配置類 就是調用不同配置的init方法
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
        for (SecurityConfigurer<O, B> configurer : configurers) {
            configurer.init((B) this);
        }
        for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
            configurer.init((B) this);
        }
    }

針對不同配置 Configurers不一樣,如果是WebSecurity則getConfigures是 WebSecurityConfigurerAdapter 所以調用的WebSecurityConfigurerAdapter的init方法<6>

<4>

org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#configure

    @SuppressWarnings("unchecked")
    private void configure() throws Exception {
        Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
        for (SecurityConfigurer<O, B> configurer : configurers) {
            //不同的配置類就是調用不同的 configure方法初始化
            configurer.configure((B) this);
        }
    }

針對不同配置 Configurers不一樣

1.如果是WebSecurity則getConfigures是 WebSecurityConfigurerAdapter 所以調用的WebSecurityConfigurerAdapter的configure方法<5>

2.如果是HttpSecurity則是<11>處配置的各種config如 如果有需要可以研究各個config如何初始化的比如我們參考HeaderConfigure的實現<12>

3.針對DefaultPasswordEncoderAuthenticationManagerBuilder 的confgure請看<15>

<15>

 

 

<5>

一般我們都會重寫

   @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

 

<6>

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init

 @Override
    public void init(WebSecurity web) throws Exception {
        //<7>初始化HttpSecurity 本事也是一個build
        HttpSecurity http = getHttp();
        //將HttpSecurity add 到WebSecurity 后續會使用securityFilterChainBuilders 
        web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
            FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
            web.securityInterceptor(securityInterceptor);
        });
    }

<7>

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#getHttp

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected final HttpSecurity getHttp() throws Exception {
        if (this.http != null) {
            return this.http;
        }
        //從容器獲取AuthenticationEventPublisher
        AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
        //設置到localConfigureAuthenticationBldr build
        this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
        //<8>通過localConfigureAuthenticationBldr build初始化AuthenticationManager 這里是org.springframework.security.authentication.ProviderManager
        AuthenticationManager authenticationManager = authenticationManager();
        //給authenticationBuilder 設置authenticationManager
        this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
        Map<Class<?>, Object> sharedObjects = createSharedObjects();
        //初始化HttpSecurity
        this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
        if (!this.disableDefaults) {
            //<11>進行默認配置
            applyDefaultConfiguration(this.http);
            ClassLoader classLoader = this.context.getClassLoader();
            List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
                    .loadFactories(AbstractHttpConfigurer.class, classLoader);
            for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
                this.http.apply(configurer);
            }
        }
        /**
         * <9>傳入我們的http build 讓我們可以做定制化配置
         *  @Override
         *     protected void configure(HttpSecurity http) throws Exception{
         *       ....
         *     }
         */
        configure(this.http);
        return this.http;
    }

 

<8>

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManager

    protected AuthenticationManager authenticationManager() throws Exception {
        //避免重復初始化
        if (!this.authenticationManagerInitialized) {
            /**
             * 這里就是調用自定義繼承WebSecurityConfigurerAdapter重寫的configure(AuthenticationManagerBuilder auth)
             * 傳入build 讓我們可以自定義一些參數配置 比如配置用戶信息是基於應用 還是內存
             *  auth.inMemoryAuthentication()
             *                 .withUser("liqiang").password("liqiang").roles("admin")
             *                 .and()
             *                 .withUser("admin").password("admin").roles("admin");
             */
            configure(this.localConfigureAuthenticationBldr);
            //
            if (this.disableLocalConfigureAuthenticationBldr) {
                //這里是一個擴展點我們可以直接 authenticationConfiguration是根據spring容器初始化的 根據authenticationConfiguration而不是通過build
                this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
            }
            else {
                //正常是走得這個build方法 build authenticationManager  這里會調用<1> 為何到1請看下面說明
                //默認 localConfigureAuthenticationBldr是DefaultPasswordEncoderAuthenticationManagerBuilder
                //初始化處 詳看:<14>
                this.authenticationManager = this.localConfigureAuthenticationBldr.build();
            }
            this.authenticationManagerInitialized = true;
        }
        return this.authenticationManager;
    }

 

 

configure(this.localConfigureAuthenticationBldr);

這里需要強調的一點是調用我們繼承的WebSecurityConfigurerAdapter 我們可以定義用戶管理器

如基於應用內存

內部創建inMemoryAuthentication方法 創建InMemoryUserDetailsManagerConfigurer 到build confgures

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         *  inMemoryAuthentication 開啟在內存中定義用戶
         *  多個用戶通過and隔開
         */
        auth.inMemoryAuthentication()
                .withUser("liqiang").password("liqiang").roles("admin")
                .and()
                .withUser("admin").password("admin").roles("admin");
    }

自定義userDetail

內部創建DaoAuthenticationConfigurer 到build confgures

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         *  inMemoryAuthentication 開啟在內存中定義用戶
         *  多個用戶通過and隔開
         */
        auth.userDetailsService(new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
                return null;
            }
        });
    }

基於封裝的jdbc查詢jdbcAuthentication方法 創建JdbcUserDetailsManagerConfigurer 到build confgures

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         *  inMemoryAuthentication 開啟在內存中定義用戶
         *  多個用戶通過and隔開
         */
        auth.jdbcAuthentication().dataSource(null).usersByUsernameQuery("");
    }

他們本質都是根據auth.創建不同的config對象 設置到build的configures  

 

<9>

    /**
     * 對於不需要授權的靜態文件放行
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

<10>

org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild

securityFilterChains為什么是列表

因為不同的url可以有不同的處理邏輯

 

  @Override
    protected Filter performBuild() throws Exception {
        Assert.state(!this.securityFilterChainBuilders.isEmpty(),
                () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
                        + "Typically this is done by exposing a SecurityFilterChain bean "
                        + "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
                        + "More advanced users can invoke " + WebSecurity.class.getSimpleName()
                        + ".addSecurityFilterChainBuilder directly");
        /**
         * 得我們設置的忽略檢查為他們添加一個 這里會添加3個chains 根據匹配做不通過處理
         *  public void configure(WebSecurity web) throws Exception {
         *         web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
         *     }
         */
        int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
        List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
        for (RequestMatcher ignoredRequest : this.ignoredRequests) {
            securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
        }
        //securityFilterChainBuilders為HttpSecurity<6>處初始化
        for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
            //執行build<1> 最終會構建成<13>
            securityFilterChains.add(securityFilterChainBuilder.build());
        }
        //通過FilterChainProxy 代理管理 它實現了ServletFilter 通過FilterChainProxy為Servlet入口 進入security的自己的filter
        FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
        if (this.httpFirewall != null) {
            filterChainProxy.setFirewall(this.httpFirewall);
        }
        if (this.requestRejectedHandler != null) {
            filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
        }
        filterChainProxy.afterPropertiesSet();

        Filter result = filterChainProxy;
        if (this.debugEnabled) {
           
            result = new DebugFilter(filterChainProxy);
        }
        this.postBuildAction.run();
        //返回filter 我們請求都會到filterChainProxy  通過他調用security的filter實現securityfilter 注入邏輯
        return result;
    }

<11>

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#applyDefaultConfiguration

  private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
        //http本質也是build 這里都是配置默認的config configure add CsrfConfigurer
        http.csrf();
        //默認增加一個WebAsyncManagerIntegrationFilter
        http.addFilter(new WebAsyncManagerIntegrationFilter());
        //configures add ExceptionHandlingConfigurer
        http.exceptionHandling();
        //configures add HeadersConfigurer
        http.headers();
        //configures add SessionManagementConfigurer
        http.sessionManagement();
        //configure add SecurityContextConfigurer
        http.securityContext();
        //configure add RequestCacheConfigurer
        http.requestCache();
        ///configure add AnonymousConfigurer
        http.anonymous();
        ///configure add ServletApiConfigurer
        http.servletApi();
        //自定義默認config
        http.apply(new DefaultLoginPageConfigurer<>());
        //configure LogoutConfigurer
        http.logout();
    }

<12>

org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#configure

   @Override
    public void configure(H http) {
        //創建一個HeaderFilter
        HeaderWriterFilter headersFilter = createHeaderWriterFilter();
        //添加到HttpSecurityFilter
        http.addFilter(headersFilter);
    }

<13>

org.springframework.security.config.annotation.web.builders.HttpSecurity#performBuild

@Override
    protected DefaultSecurityFilterChain performBuild() {
        //將httpSecurity filter排序
        this.filters.sort(OrderComparator.INSTANCE);
        List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
        for (Filter filter : this.filters) {
            sortedFilters.add(((OrderedFilter) filter).filter);
        }
        //requestMatcher 為匹配條件  DefaultSecurityFilterChain 包裝起來 管理所有Filter
        return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
    }

<14>

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#setApplicationContext

@Autowired
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
        ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
        LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
        this.authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder); this.localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder( objectPostProcessor, passwordEncoder) { @Override public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) { WebSecurityConfigurerAdapter.this.authenticationBuilder.eraseCredentials(eraseCredentials); return super.eraseCredentials(eraseCredentials); } @Override public AuthenticationManagerBuilder authenticationEventPublisher( AuthenticationEventPublisher eventPublisher) { WebSecurityConfigurerAdapter.this.authenticationBuilder.authenticationEventPublisher(eventPublisher); return super.authenticationEventPublisher(eventPublisher); } };
    }

<15>

跟WebSecurityConfigurerAdapter 一樣 以下3個方法都是add不同的config 執行不同的初始化邏輯

static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {

        private PasswordEncoder defaultPasswordEncoder;

        /**
         * Creates a new instance
         * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
         */
        DefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
                PasswordEncoder defaultPasswordEncoder) {
            super(objectPostProcessor);
            this.defaultPasswordEncoder = defaultPasswordEncoder;
        }

        @Override
        public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
                throws Exception {
            return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
        }

        @Override
        public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
            return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
        }

        @Override
        public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
                T userDetailsService) throws Exception {
            return super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);
        }

    }

<16>

org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder#performBuild

    @Override
    protected ProviderManager performBuild() throws Exception {
        if (!isConfigured()) {
            this.logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
            return null;
        }
        //通過ProviderManager 統一管理providers authenticationProviders 都可以定制
        ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
                this.parentAuthenticationManager);
        if (this.eraseCredentials != null) {
            providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
        }
        if (this.eventPublisher != null) {
            providerManager.setAuthenticationEventPublisher(this.eventPublisher);
        }
        //依賴注入
        providerManager = postProcess(providerManager);
        return providerManager;
    }

 


免責聲明!

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



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