随笔 - 70  文章 - 0  评论 - 2  阅读 - 79274

spring security 源码学习(三)WebSecurityConfiguration

本篇目标是解析WebSecurityConfiguration是如何初始化的

首先,看下他的源码。

  如这个类开头的注释写的一样,这个类的最后会生成一个FilterChainProxy类(一个Fliter),作为过滤器(链)来处理一个请求进入spring后进行的认证操作。我们挑着这里面比较重要的步骤进行分析下。

(一)AutowiredWebSecurityConfigurersIgnoreParents初始化

 这个类有一个获取所有WebSecurityConfigurer子类实例的方法

 这个类的用途在下面

(二)WebSecurity实例化以及基本配置的设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
        ObjectPostProcessor<Object> objectPostProcessor,<br>               //利用上面的初始化的AutowiredWebSecurityConfigurersIgnoreParents的getWebSecurityConfigurers方法获取我们的Spring security的配置
        @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
        throws Exception {<br>          //创建WebSecurity实例
    webSecurity = objectPostProcessor
            .postProcess(new WebSecurity(objectPostProcessor));
    if (debugEnabled != null) {
        webSecurity.debug(debugEnabled);
    }
 
    Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
 
    Integer previousOrder = null;
    Object previousConfig = null;
    for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
        Integer order = 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;
    }
    for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
        webSecurity.apply(webSecurityConfigurer);
    }
    this.webSecurityConfigurers = webSecurityConfigurers;
}

  这个方法主要的操作是实例化WebSecurity,并将Spring security的配置设置为他的webSecurityConfigurers属性的值

    这边为了方便查看之前的配置,我们把配置类的代码也插入到这边

  (三)springSecurityFilterChain初始化

  就是这个方法,将最核心的Spring security的过滤器(链)初始化了。

  这个方法的第一步就是判断当前是否配置了webSecurityConfigurers,如果没有,则会生成一个默认的:new WebSecurityConfigurerAdapter(),这个也就可以解释我刚接触Spring security时的困惑(为什么引入了Spring security的依赖后,我基本的配置类没写,我的接口就不能调用了?答:这边给了个默认的配置)

  第二步就会对webSecurity进行构建:webSecurity.build();

  首先调用的是AbstractSecurityBuilder的build()方法

  然后调用doBuild()方法,doBuild()是在AbstractSecurityBuilder的子类AbstractConfiguredSecurityBuilder中实现的

  这是一个很典型的模版方法模式,其中的beforeInit()和beforeConfigure()皆为钩子方法,这里默认也没有任何实现,我们暂时不用关注,主要需要注意的是init()、configure()和performBuild()方法。下面我们一个个分析这几个方法。

1、init方法

  这边的configurer.init((B) this);调用的是WebSecurityConfigurerAdapter的init方法,源码如下

1
2
3
4
5
6
7
8
9
10
public void init(final WebSecurity web) throws Exception {
    final HttpSecurity http = getHttp();(1
    web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
        public void run() {
            FilterSecurityInterceptor securityInterceptor = http
                    .getSharedObject(FilterSecurityInterceptor.class);
            web.securityInterceptor(securityInterceptor);
        }
    });(2
}

  第(1)步是初始化了HttpSecurity,第(2)步的前半部分web.addSecurityFilterChainBuilder(http)则将这个HttpSecurity加入到了webSecurity的

1
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();

  这个属性中,后面performBuild的时候会用到;

  这里的第(1)步其实很关键,一下子初始化了2个核心。这里再多说一句,针对不同的WebSecurityConfigurerAdapter的实现类,也就是配置,他们各自的HttpSecurity是各自的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
protected final HttpSecurity getHttp() throws Exception {
    if (http != null) {
        return http;
    }
 
    DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
            .postProcess(new DefaultAuthenticationEventPublisher());
    localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
 
    AuthenticationManager authenticationManager = authenticationManager();(1
    authenticationBuilder.parentAuthenticationManager(authenticationManager);
    Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
 
    http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
            sharedObjects);(2
    if (!disableDefaults) {
        // @formatter:off
        http
            .csrf().and()(3
            .addFilter(new WebAsyncManagerIntegrationFilter())(4
            .exceptionHandling().and()(5
            .headers().and()(6
            .sessionManagement().and()(7
            .securityContext().and()(8
            .requestCache().and()(9
            .anonymous().and()(10
            .servletApi().and()(11
            .apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()(12
            .logout();(13
        // @formatter:on
        ClassLoader classLoader = this.context.getClassLoader();
        List<AbstractHttpConfigurer> defaultHttpConfigurers =
                SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
 
        for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
            http.apply(configurer);
        }
    }
    configure(http);
    return http;
}

  这个方法的第(1)步获取了AuthenticationManager,而如果AuthenticationManager没有被初始化,则这里则对AuthenticationManager进行了初始化,后面的第(2)步则实例化了HttpSecurity,第(3)-(13)步则给HttpSecurity加了些configure和给WebSecurity加了些Filter。下面我们着重看第(1)步,后面的几步会在后面的不同Filter的文章中分别交代。下面看下authenticationManager()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected AuthenticationManager authenticationManager() throws Exception {
    if (!authenticationManagerInitialized) {<br>              //提供了自定义配置的地方,如果在我们的配置类中override了configure(AuthenticationManagerBuilder auth)方法,则就是在这里生效的
        configure(localConfigureAuthenticationBldr);
        if (disableLocalConfigureAuthenticationBldr) {<br>                    //提供了默认的配置AuthenticationManager的方法,一般我们什么都不配置,则走的这里的方法
            authenticationManager = authenticationConfiguration
                    .getAuthenticationManager();
        }
        else {
            authenticationManager = localConfigureAuthenticationBldr.build();
        }
        authenticationManagerInitialized = true;
    }
    return authenticationManager;
}

  这个方法的作用写在了注释中,我们继续看authenticationConfiguration.getAuthenticationManager();方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public AuthenticationManager getAuthenticationManager() throws Exception {
        if (this.authenticationManagerInitialized) {
            return this.authenticationManager;
        }<br>          //new了一个AuthenticationManagerBuilder实例,执行了new AuthenticationManagerBuilder(objectPostProcessor)
        AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(
                this.objectPostProcessor);
        if (this.buildingAuthenticationManager.getAndSet(true)) {
            return new AuthenticationManagerDelegator(authBuilder);
        }
          
        for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {(1
            authBuilder.apply(config);
        }
          
        authenticationManager = authBuilder.build();(2
 
        if (authenticationManager == null) {
            authenticationManager = getAuthenticationManagerBean();
        }
 
        this.authenticationManagerInitialized = true;
        return authenticationManager;
    }

  这边核心的有2步,第(1)步是获取配置,第(2)步是我们熟悉的初始化节奏。而第一步的配置,默认有如下的配置,他们具体从哪来的,我们留到后面再讲解。

 

 

 那么第(2)步就来到了AuthenticationManagerBuilder的build(),继而就是doBuild(),也就是我们上面的模版方法,这边我们再贴一次代码

  这边最终返回的是ProviderManager,上面的init和configur我们先跳过分析,直接看这边的performBuild()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected ProviderManager performBuild() throws Exception {
    if (!isConfigured()) {
        logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
        return null;
    }
    ProviderManager providerManager = new ProviderManager(authenticationProviders,
            parentAuthenticationManager);
    if (eraseCredentials != null) {
        providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
    }
    if (eventPublisher != null) {
        providerManager.setAuthenticationEventPublisher(eventPublisher);
    }
    providerManager = postProcess(providerManager);
    return providerManager;
}

  这边可以看的出来ProviderManager是new出来的,而他最重要的则是后面的参数authenticationProviders,也就是List<AuthenticationProvider> authenticationProviders,他就是进行鉴权的核心实现。

 AuthenticationManager的初始化,我们就先到这里,后面会有一篇文章再对这个过程进行详细梳理。

2、configure(WebSecurity web)方法

这边是对WebSecurity进行定制化的地方,这个方法相对init()来说就简单多了

默认来讲,如果我们在我们的配置类中未override这个方法,那么这里将什么都不发生,默认实现如下。

1
2
public void configure(WebSecurity web) throws Exception {
}

这里我们可以配置一些需要忽略的,也就是不需要认证的请求,如:

1
2
3
4
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/test/**");
}

  这边的配置和下面的ignoredRequests.size()相对应。

 

3、performBuild() 

这个方法将会真正的实例化Filter并返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
  protected Filter performBuild() throws Exception {
    Assert.state(
        !securityFilterChainBuilders.isEmpty(),
        "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
            + WebSecurity.class.getSimpleName()
            + ".addSecurityFilterChainBuilder directly");
    int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
    List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
        chainSize);
    for (RequestMatcher ignoredRequest : ignoredRequests) {
      securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
    }<br>    //
    for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
      securityFilterChains.add(securityFilterChainBuilder.build());(1
    }
    FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);(2)
    if (httpFirewall != null) {
      filterChainProxy.setFirewall(httpFirewall);
    }
    filterChainProxy.afterPropertiesSet();
 
    Filter result = filterChainProxy;
    if (debugEnabled) {
      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);
    }
    postBuildAction.run();
    return result;
  }

这里的第(1)步则是实例化SecurityFilterChain,加入到List<SecurityFilterChain> filterChains中,这里的securityFilterChainBuilder.build()实质上就是HttpSecurity进行build,这里的list中有多少值就看系统配置了多少个WebSecurityConfigurerAdapter,按照我们之前的配置,这里就一个。根据下面的类图,也就是调用的AbstractSecurityBuilder的build()方法

 

 

 HttpSecurity的build我们发到下一篇文章。

第(2)步则将List<SecurityFilterChain> filterChains作为FilterChainProxy初始化的属性,初始化了FilterChainProxy,并返回了这个Filter。

至此,FilterChainProxy的初始化结束了,也就是Spring security的初始化结束了。

  

  

 

posted on   幽人月  阅读(1082)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示