SpringSecurity源码之WebSecurity构建FilterChainProxy

主要参考了https://mp.weixin.qq.com/s/D0weIKPto4lcuwl9DQpmvQ

SpringSecurity版本是2.7.9。将SpringBoot和SpringSecurity结合使用,SpringSecurity自动配置类是SecurityAutoConfiguration.class。

 

@AutoConfiguration
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
    public SecurityAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean({AuthenticationEventPublisher.class})
    public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
        return new DefaultAuthenticationEventPublisher(publisher);
    }
}

SecurityAutoConfiguration导入了SpringBootWebSecurityConfiguration配置类。
 

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
class SpringBootWebSecurityConfiguration {
    SpringBootWebSecurityConfiguration() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnMissingBean(
        name = {"springSecurityFilterChain"}
    )
    @ConditionalOnClass({EnableWebSecurity.class})
    @EnableWebSecurity
    static class WebSecurityEnablerConfiguration {
        WebSecurityEnablerConfiguration() {
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({WebInvocationPrivilegeEvaluator.class})
    @ConditionalOnBean({WebInvocationPrivilegeEvaluator.class})
    static class ErrorPageSecurityFilterConfiguration {
        ErrorPageSecurityFilterConfiguration() {
        }

        @Bean
        FilterRegistrationBean<ErrorPageSecurityFilter> errorPageSecurityFilter(ApplicationContext context) {
            FilterRegistrationBean<ErrorPageSecurityFilter> registration = new FilterRegistrationBean(new ErrorPageSecurityFilter(context), new ServletRegistrationBean[0]);
            registration.setDispatcherTypes(DispatcherType.ERROR, new DispatcherType[0]);
            return registration;
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnDefaultWebSecurity
    static class SecurityFilterChainConfiguration {
        SecurityFilterChainConfiguration() {
        }

        @Bean
        @Order(2147483642)
        SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
            ((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated();
            http.formLogin();
            http.httpBasic();
            return (SecurityFilterChain)http.build();
        }
    }
}

 

SecurityFilterChainConfiguration是默认的SecurityFilterChain配置类。WebSecurityEnablerConfiguration类上@EnableWebSecurity做了自动化配置Security的主要工作。

 

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

@EnableWebSecurity注解导入了WebSecurityConfiguration配置类,同时引入了@EnableGlobalAuthentication注解。来看下WebSecurityConfiguration类。
 

WebSecurityConfiguration实现了ImportAware,BeanClassLoaderAware两个接口。BeanClassLoaderAware主要是为了获取ClassLoader。ImportAware的作用在松哥的博客说了。是为了获取到 @EnableWebSecurity中的属性值,这里主要是 debug 属性。WebSecurityConfiguration主要看springSecurityFilterChain(),setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, ConfigurableListableBeanFactory beanFactory)方法。先看下setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, ConfigurableListableBeanFactory beanFactory)

@Autowired(
    required = false
)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, ConfigurableListableBeanFactory beanFactory) throws Exception {
    this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
    if (this.debugEnabled != null) {
        this.webSecurity.debug(this.debugEnabled);
    }

    List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = (new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory)).getWebSecurityConfigurers();
    webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
    Integer previousOrder = null;
    Object previousConfig = null;

    Iterator var6;
    SecurityConfigurer config;
    for(var6 = webSecurityConfigurers.iterator(); var6.hasNext(); previousConfig = config) {
        config = (SecurityConfigurer)var6.next();
        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;
    }

    var6 = webSecurityConfigurers.iterator();

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

    this.webSecurityConfigurers = webSecurityConfigurers;
}

setFilterChainProxySecurityConfigurer方法主要做了以下几件事:
1、创建WebSecurity,并调用objectPostProcessor.postProcess将WebSecurity走了一遍Spring的创建bean流程。
2、获取SecurityConfigurer,SecurityConfigurer是为了配置SpringSecurity的类。并按照Order排序,校验了SecurityConfigurer集合之间的Order不能有相同的值。
3、将SecurityConfigurer集合加入到WebSecurity的configurers属性中,并将SecurityConfigurer集合设置到webSecurityConfigurers属性。

 

再来看springSecurityFilterChain()方法:

@Bean(
    name = {"springSecurityFilterChain"}
)
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 = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
        });
        this.webSecurity.apply(adapter);
    }

    Iterator var7 = this.securityFilterChains.iterator();

    while(true) {
        while(var7.hasNext()) {
            SecurityFilterChain securityFilterChain = (SecurityFilterChain)var7.next();
            this.webSecurity.addSecurityFilterChainBuilder(() -> {
                return securityFilterChain;
            });
            Iterator var5 = securityFilterChain.getFilters().iterator();

            while(var5.hasNext()) {
                Filter filter = (Filter)var5.next();
                if (filter instanceof FilterSecurityInterceptor) {
                    this.webSecurity.securityInterceptor((FilterSecurityInterceptor)filter);
                    break;
                }
            }
        }

        var7 = this.webSecurityCustomizers.iterator();

        while(var7.hasNext()) {
            WebSecurityCustomizer customizer = (WebSecurityCustomizer)var7.next();
            customizer.customize(this.webSecurity);
        }

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

主要做了几件事:
1、判断是否存在自定义的SecurityConfigurer和SecurityFilterChain,不存在则创建一个WebSecurityConfigurerAdapter。
2、将已存在的SecurityFilterChain集合WebSecurity的securityFilterChainBuilders属性中
3、调用WebSecurityCustomizer的自定义方法自定义WebSecurity
4、调用webSecurity的build方法
 
webSecurity.build方法会调用到AbstractConfiguredSecurityBuilder#doBuild()方法:

protected final O doBuild() throws Exception {
    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;
    }
}

设置了构建的生命周期,调用了几个方法。主要看init(),configure(),performBuild()三个方法。

 
AbstractConfiguredSecurityBuilder#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);
    }

}

获取SecurityConfigurer类并执行init方法。这里的SecurityConfigurer是WebSecurityConfigurerAdapter。

WebSecurityConfigurerAdapter#init(WebSecurity web)

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

创建了HttpSecurity,并将HttpSecurity添加到WebSecurity的securityFilterChainBuilders属性。同时设置postBuildAction属性,postBuildAction是构建完后执行的操作。

 

WebSecurityConfigurerAdapter#getHttp()

    protected final HttpSecurity getHttp() throws Exception {
    if (this.http != null) {
        return this.http;
    } else {
        AuthenticationEventPublisher eventPublisher = this.getAuthenticationEventPublisher();
        this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
        AuthenticationManager authenticationManager = this.authenticationManager();
        this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
        Map<Class<?>, Object> sharedObjects = this.createSharedObjects();
        this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
        if (!this.disableDefaults) {
            this.applyDefaultConfiguration(this.http);
            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);
            }
        }

        this.configure(this.http);
        return this.http;
    }
}

调用this.authenticationManager()构建了AuthenticationManager,createSharedObjects创建了共享对象,创建了HttpSecurity。调用applyDefaultConfiguration为HttpSecurity设置默认的配置。从Spring.factorys文件中加载AbstractHttpConfigurer并应用在HttpSecurity。最后调用configure(HttpSecurity http)。一般会重写configure(HttpSecurity http)方法自定义安全的配置。重写的配置会覆盖默认的配置

createSharedObjects()

private Map<Class<?>, Object> createSharedObjects() {
    Map<Class<?>, Object> sharedObjects = new HashMap();
    sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());
    sharedObjects.put(UserDetailsService.class, this.userDetailsService());
    sharedObjects.put(ApplicationContext.class, this.context);
    sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
    sharedObjects.put(AuthenticationTrustResolver.class, this.trustResolver);
    return sharedObjects;
}

默认的DetailsService是UserDetailsServiceDelegator,AuthenticationTrustResolver是AuthenticationTrustResolverImpl。AuthenticationTrustResolver判断是否是匿名登录或是RememberMe登录。

 

applyDefaultConfiguration(HttpSecurity http)

private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
    http.csrf();
    http.addFilter(new WebAsyncManagerIntegrationFilter());
    http.exceptionHandling();
    http.headers();
    http.sessionManagement();
    http.securityContext();
    http.requestCache();
    http.anonymous();
    http.servletApi();
    http.apply(new DefaultLoginPageConfigurer());
    http.logout();
}

csrf()将CsrfConfigurer配置类加入到HttpSecurity的configurers属性中。exceptionHandling()方法加入了ExceptionHandlingConfigurer配置类。headers()加入了HeadersConfigurer配置类。sessionManagement()加入了SessionManagementConfigurer配置类。securityContext()加入了SecurityContextConfigurer配置类。requestCache()加入了RequestCacheConfigurer配置类。anonymous()加入了AnonymousConfigurer配置类。servletApi()加入了ServletApiConfigurer配置类。加入了DefaultLoginPageConfigurer配置类。logout()加入了LogoutConfigurer配置类。

 

configure()调用SecurityConfigurer集合的configure(WebSecurity web)方法。

 

performBuild()方法创建并配置FilterChainProxy:

    protected Filter performBuild() throws Exception {
    Assert.state(!this.securityFilterChainBuilders.isEmpty(), () -> {
        return "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this is done by exposing a SecurityFilterChain bean. More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly";
    });
    int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
    List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize);
    List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList();
    Iterator var4 = this.ignoredRequests.iterator();

    while(var4.hasNext()) {
        RequestMatcher ignoredRequest = (RequestMatcher)var4.next();
        this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest + ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
        SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]);
        securityFilterChains.add(securityFilterChain);
        requestMatcherPrivilegeEvaluatorsEntries.add(this.getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
    }

    var4 = this.securityFilterChainBuilders.iterator();

    while(var4.hasNext()) {
        SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var4.next();
        SecurityFilterChain securityFilterChain = (SecurityFilterChain)securityFilterChainBuilder.build();
        securityFilterChains.add(securityFilterChain);
        requestMatcherPrivilegeEvaluatorsEntries.add(this.getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
    }

    if (this.privilegeEvaluator == null) {
        this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(requestMatcherPrivilegeEvaluatorsEntries);
    }

    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) {
        this.logger.warn("\n\n********************************************************************\n**********        Security debugging is enabled.       *************\n**********    This may include sensitive information.  *************\n**********      Do not use in a production system!     *************\n********************************************************************\n\n");
        result = new DebugFilter(filterChainProxy);
    }

    this.postBuildAction.run();
    return (Filter)result;
}

ignoredRequests是不走Spring Security Filter过滤器的请求,securityFilterChainBuilders是走Spring Security Filter过滤器的请求。分别加入到SecurityFilterChain中。调用securityFilterChainBuilder.build()创建Spring Security 过滤器链SecurityFilterChain。再将所有的SecurityFilterChain加入到FilterChainProxy中。

posted @ 2023-04-09 20:58  shigp1  阅读(484)  评论(0)    收藏  举报