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;
    }
原文地址:https://www.cnblogs.com/LQBlog/p/15508248.html