2023-06-12  阅读(3)
原文作者:发飙的蜗牛咻咻咻~ 原文地址:https://blog.csdn.net/qq_36221788/category_11009647.html

〇、上篇回顾

  • 整个框架的核心就是构建一个名字为 springSecurityFilterChain 的过滤器,它的类型是 FilterChainProxy
  • 框架的主要参与者是 建造者配置器 ,其中 WebSecurityHttpSecurity 都是 建造者
  • WebSecurity 的构建目标是 FilterChainProxy 对象,即核心过滤器 springSecurityFilterChain
  • HttpSecurity 的构建目标只是 FilterChainProxy 对象中一组 SecurityFilterChain 的一个
  • 配置器 主要关注 init()configure() 方法

一、WebSecurityConfiguration

第一篇《Spring Security源码(一):整体框架设计》文章提到,看源码可以从 @EnableWebSecurity 注解开始,点进去发现它引入了类 WebSecurityConfiguration,类中正好有生成核心过滤器的Bean方法 springSecurityFilterChain() ,还需要关注类中另一个方法 setFilterChainProxySecurityConfigurer(),一起来看看。

setFilterChainProxySecurityConfigurer()

  • 创建 WebSecurity 建造者对象,apply() 初始配置。
    	@Autowired(required = false)
    	public void setFilterChainProxySecurityConfigurer(
    			ObjectPostProcessor<Object> objectPostProcessor,
    			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
    			throws Exception {
    		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;
    	}

springSecurityFilterChain()

  • 建造者 webSecurity 调用 build() 方法,开始构建 springSecurityFilterChain
    	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    	public Filter springSecurityFilterChain() throws Exception {
    		boolean hasConfigurers = webSecurityConfigurers != null
    				&& !webSecurityConfigurers.isEmpty();
    		if (!hasConfigurers) {
    			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
    					.postProcess(new WebSecurityConfigurerAdapter() {
    					});
    			webSecurity.apply(adapter);
    		}
    		return webSecurity.build();
    	}

二、FilterChainProxy的创建过程

创建过程

  • springSecurityFilterChain() 方法中,webSecurity 调用了 build() 方法
  • 方法进去继续调用了 doBuild(),该方法依次调用各个配置器的 init()configure() 方法
  • 等配置到位后,开始执行 performBuild(),进行核心过滤器 FilterChainProxy 的创建
    	@Override
    	protected final O doBuild() throws Exception {
    		synchronized (configurers) {
    			buildState = BuildState.INITIALIZING;
    
    			beforeInit();
    			init();
    
    			buildState = BuildState.CONFIGURING;
    
    			beforeConfigure();
    			configure();
    
    			buildState = BuildState.BUILDING;
    
    			O result = performBuild();
    
    			buildState = BuildState.BUILT;
    
    			return result;
    		}
    	}
  • webSecurity 调用的 performBuild() 方法就创建了核心过滤器 FilterChainProxy

注意创建过程可不是这么简单的由 webSecurity 一次性调用下来,就是说 doBuild() 可不只是被 webSecurity 对象调用,会被很多创建者重复调用,重复的去走创建流程,比如被一堆的 HttpSecurity 调用目的是创建单条过滤器链,可以自己Debug去走一走。

performBuild() 介绍

回顾第二章《Spring Security源码(二):建造者详解》,该方法被 WebSecurityHttpSecurityAuthenticationManagerBuilder 三个建造者所实现,此篇先不关心后者。

  • HttpSecurity#performBuild:创建核心过滤器中单条过滤器链:SecurityFilterChain
    	@Override
    	protected DefaultSecurityFilterChain performBuild() throws Exception {
    		Collections.sort(filters, comparator);
    		return new DefaultSecurityFilterChain(requestMatcher, filters);
    	}
  • WebSecurity#performBuild:创建核心过滤器:FilterChainProxy
    	@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));
    		}
    		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
    			securityFilterChains.add(securityFilterChainBuilder.build());
    		}
    		FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
    		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;
    	}

securityFilterChainBuilders 什么时候插入的?

上面 WebSecurity#performBuild 方法中循环所有的 securityFilterChainBuilders 创建单条过滤器链,其中的数据是什么时候插入的?


三、Debug

结果分析

  • 可以看到核心过滤器 FilterChainProxy 有两个过滤器链,其中一个就是我们自己定义的,有14个过滤器,部分是我们定义的部分是系统默认的,仔细发现两个过滤器链最后一个过滤器都是 FilterSecurityInterceptor

    202306122224419241.png

FilterSecurityInterceptor

  • 这个类是在 WebSecurityConfigurerAdapter#init 方法中创建的,创建了一个新线程,最后在 WebSecurity#performBuild 中启动该线程 postBuildAction.run(); 将这个过滤器添加到每个过滤器链的末位,在认证过程中保证该过滤器是最后执行的。
  • 这个过滤器是整个链条的最后一环,过了它就可以访问我们后台的资源服务了。
  • Spring Security的访问控制功能,它起到了至关重要的作用,后面文章来对它进行详细解析。

四、总结

  • 其实找到源码入口,跟踪方法调用链,再去了解下相关的一些建造者、配置器类,也就基本知道核心过滤器是怎么创建的了。

五、系列文章

Spring Security 系列

Spring Security OAuth 系列


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文