Spring Security Java Config 浅析

在之前的项目中使用接触过通过 xml 配置使用 Spring Security。但是最近比较流行 Java Config来配置使用 ``Spring Security。并且对它的 builder 模式比较感兴趣,就来查看了它的源码实现。下面就把我自己的分析结果记录下来,希望对阅读这篇博客的你也有所帮助。在 Spring Security 里面有两个非常重要的概念认证与授权。

  • 认证:是确认某主体在某系统中是否合法、可用的过程。这里的主体既可以是登陆系统的用户也可以是接入 的设备或者其他系统
  • 授权:是指当前主体通过认证之后,是否允许其执行某项操作的过程

首先我们来认识一下 Spring Security 通过 Java 配置中的几个重要的类。(基于 Spring Security 5.2.2-RELEASE)

1、 Spring Security 配置中几个重要的类

下面以 Spring Security 中几个重要的类给大家简单的介绍一下它们的功能。不然在后面的源码讲解当中大家可能会迷失到 Spring Security 的类中。

1.1 FilterChainProxy

FilterChainProxy : Spring Security 实现原理是通过遍历它的内部对象 SecurityFilterChain 列表。它的内部有两个方法一个是 SecurityFilterChain#match ,另一个就是获取 Java Servlet Api 的中 Filter 链表的SecurityFilterChain#getFilters。当有外部请求来的时候,通过 match 方法来匹配请求参数 HttpServletRequest 。如果这个 SecurityFilterChain 匹配请求HttpServletRequest 。那么就通过 SecurityFi`lterChain#getFilters 获取当 Filter 列表来对请求实现认证与授权。而 FilterChainProxy 是通过 WebSecurityConfiguration#springSecurityFilterChain 进行创建的。

1.2 WebSecurityConfiguration

WebSecurityConfiguration : WebSecurityConfiguration 用于初始化 WebSecurity 配置。首先,在 WebSecurityConfiguration#setFilterChainProxySecurityConfigurer 方法中,它以配置 Spring Security 时候继承自 WebSecurityConfigurerAdapter 的配置类来初始化一个 SecurityConfigurer 列表, Spring Security 以 SecurityConfigurer 列表为依据,启用所需的安全策略。

它通过 @EnableWebSecurity 激活,并且@EnableWebSecurity 还有一个 debug 参数用于指定是否采用调式模式,默认是 false。在调式模式下,请个请求的详细信息和所经过的过滤器,甚至其实调用栈都会被打印到控制台。

1.3 HttpSecurity

HttpSecurity : HttpSecurity 就是 Spring Security Xml 配置对应 http 命名空间中元素。它允许为特定的http配置基于web的安全性请求。默认情况下,它将应用于所有请求,但可以使用 requestMatcher(requestMatcher) 或其他类似的方法进行限制。

下面可以看到最基本的基于表单的配置。配置将要求任何被请求的URL都需要一个角色为“ROLE_USER”的用户。它还定义了一个具有用户名的内存中的身份验证方案user、密码password和角色ROLE_USER。更多的例子,在HttpSecurity中引用各个方法的Java文档。

 @Configuration
 @EnableWebSecurity
 public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
 
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  		http.authorizeRequests().antMatchers("/**).hasRole(“/USER”).and().formLogin();
  	}
 
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
  	}
}

1.4 WebSecurity

WebSecurityWebSecurityConfiguration#springSecurityFilterChain 中通过 WebSecurity#build 来创建 FilterChainProxy。也就是 Spring Security 的拦截器 Filter 链。

WebSecurity 实现了 SecurityBuilder 并通过 WebSecurity#performBuild 方法创建 FilterChainProxy
WebSecurity 的父类 AbstractConfiguredSecurityBuilder#doBuild 中,它的AbstractConfiguredSecurityBuilder#init 以及 AbstractConfiguredSecurityBuilder#configure 会获取 SecurityConfigurer 来通过调用 SecurityConfigurer#initSecurityConfigurer#configureSecurityBuilder对象进行处理。

其实WebSecurity#performBuild是通过SecurityBuilder#build这个方法触发的。Spring Security 通过 Java 配置来配置 SecurityBuilderSecurityConfigurer 是非常重要的两个接口。

1.5 SecurityBuilder

SecurityBuilder是 Spring Security 定义用来 Spring Security 的对象创建接口。它只有一个接口 build,它的核心实现类就是我们上面提到的 HttpSecurityWebSecurity

public interface SecurityBuilder<O> {

	/**
	 * Builds the object and returns it or null.
	 *
	 * @return the Object to be built or null if the implementation allows it.
	 * @throws Exception if an error occurred when building the Object
	 */
	O build() throws Exception;
}

1.6 SecurityConfigurer

SecurityConfigurer接口定义了两个方法 SecurityConfigurer#initSecurityConfigurer#configureSecurityConfigurer接口的主要作用是用来配置 SecurityBuilder.所有的 SecurityConfigurer,首先调用 #init(SecurityBuilder) ,然后调用 #configure(SecurityBuilder)SecurityBuilder 进行配置。

SecurityConfigurer.java

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
	/**
	 * Initialize the {@link SecurityBuilder}. Here only shared state should be created
	 * and modified, but not properties on the {@link SecurityBuilder} used for building
	 * the object. This ensures that the {@link #configure(SecurityBuilder)} method uses
	 * the correct shared objects when building. Configurers should be applied here.
	 *
	 * @param builder
	 * @throws Exception
	 */
	void init(B builder) throws Exception;

	/**
	 * Configure the {@link SecurityBuilder} by setting the necessary properties on the
	 * {@link SecurityBuilder}.
	 *
	 * @param builder
	 * @throws Exception
	 */
	void configure(B builder) throws Exception;
}

2、@EnanbleWebSecurity

@EnanbleWebSecurity 是开启 Spring Security 的默认行为,这它通过 @Import注解导入了WebSecurityConfiguration。也就是说 WebSecurityConfiguration 通过 @EnanbleWebSecurity 得到初始化的机会。

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.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;
}

@EnanbleWebSecurity 可以通过 debug 参数用于指定是否采用调度,默认为 false。在调度模式下,每个请求的详细信息和所经过的过滤器,甚至其调用栈都会被打印到控制台。

在这里插入图片描述
当你发送一个 http 请求时,控制台会打印请求信息以及当前请求 uri 匹配 Spring Security 中的哪些过滤器列表。

3、WebSecurityConfiguration

WebSecurityConfiguration是于初始化 WebSecurity 配置类。首先通过 WebSecurityConfiguration#setFilterChainProxySecurityConfigurer 初始化 WebSecurity 对象。

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

		webSecurityConfigurers.sort(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 对象实例,然后通过 objectPostProcessor 依赖注入需要注入的对象。就是 Spring 的 DI 机制。
  • 然后把 Spring 容器中的 SecurityConfigurer 对象添加到 WebSecurity,这里需要注意的一点是,当我们使用 @EnanbleWebSecurity 标注一个类时,这个类都会继承自 WebSecurityConfigurerAdapter。而WebSecurityConfigurerAdapter实现了 SecurityConfigurer,所以这里就是把我们的配置类添加到 WebSecurity对象里面。并且最终会添加到 WebSecurity 父类 AbstractConfiguredSecurityBuilderAbstractConfiguredSecurityBuilder#configurers 属性中(圈起来会考)。

然后通过 WebSecurityConfiguration#springSecurityFilterChain 来 初始化 Spring Security 里面的最最关键的类 FilterChainProxy 这个过滤器链。

	@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内部有 FilterChainProxy#filterChains 属性,也就是 SecurityFilterChain列表。

SecurityFilterChain.java

public interface SecurityFilterChain {

	boolean matches(HttpServletRequest request);

	List<Filter> getFilters();
}

当有 http 请求来临的时候, SecurityFilterChain列表会判断这个请求是否满足条件,如果这个请求的满足条件就会获取当前SecurityFilterChain中的 javax.servlet.Filter 列表,过滤当前的请求。达到最终的认证授权效果。

4、FilterChainProxy 的创建过程

通过上面的讲解可以知道 FilterChainProxy 的创建入口是 WebSecurityConfiguration#springSecurityFilterChain。而它是通过 WebSercurity#build 来创建FilterChainProxy 对象的。 这里需要着重强调的一下就是 WebSercurity 实现最终实现了 SecurityBuilder 接口。可以回头看一下第一小节中 Spring Security 里面几个重要的类。当进行创建 FilterChainProxy 的时候最后会调用到 AbstractConfiguredSecurityBuilder#doBuild 方法。

AbstractConfiguredSecurityBuilder#doBuild`

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

下面我们就来分析一下这里面的几个方法:

  • AbstractConfiguredSecurityBuilder#beforeInit:调用SecurityConfigurer#init 方法之前的扩展方法,空实现。
  • AbstractConfiguredSecurityBuilder#init():初始化方法,会调用SecurityConfigurer#init。注意这里的 SecurityConfigurer 对象其实就是我们标注了 @EnableWebSecurity 用来进行 Java 配置 Spring Security 的类。最终会调用到 WebSecurityConfigurerAdapter#init。首先它会获取 HttpSecurity 并且通过 WebSecurity#addSecurityFilterChainBuilder添加到 WebSecurity 当中(构造 HttpSecurity 过程非常重要且复杂会在后期的章节介绍),然后把FilterSecurityInterceptor添加到 WebSecurity 当中。
  • AbstractConfiguredSecurityBuilder#beforeConfigure():调用SecurityConfigurer#configure 方法之前的扩展方法,空实现。
  • AbstractConfiguredSecurityBuilder#configure:调用标注@EnableWebSecurity 类的父类的 WebSecurityConfigurerAdapter#configure方法,空实现。
  • AbstractConfiguredSecurityBuilder#performBuild:会调用到它的子类也就是 WebSecurity#performBuild方法最终实例化 FilterChainProxy。如果@EnableWebSecurity注解中的debug() 方法标注为 true,则会创建 org.springframework.security.web.debug.DebugFilter 显示第 2 小节中的调度信息。

WebSecurity#performBuild

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

看到了这里大家就对 WebSecurity 这个类的定位非常的清晰,它其实就是一个桥梁的作用。把 HttpSecurity 这个配置类与 FilterChainProxy 这个真正的过滤 http 请求的类连接起来。当我们在使用 Spring Security 的 Java 配置的时候是通过 HttpSecurity 来进行配置的,然后 WebSecurity 把 HttpSecurity 设置到它的属性当中。WebSecurity 本身是实现了 SecurityBuilder ,所以在WebSecurityConfiguration#springSecurityFilterChain当中通过 WebSercurity#build(也就是 SecurityBuilder 定义的 build 方法)来创建FilterChainProxy 对象的。

5、HttpSecurity 的创建过程

在第 4 章节中我们提到当 WebSecurity 创建FilterChainProxy 对象的过程中会调用AbstractConfiguredSecurityBuilder#init()方法。其实就是调用标注了@EnableWebSecurity 类的父类 WebSecurityConfigurerAdapter#init 方法。

WebSecurityConfigurerAdapter#init

	public void init(final WebSecurity web) throws Exception {
		final HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
			FilterSecurityInterceptor securityInterceptor = http
					.getSharedObject(FilterSecurityInterceptor.class);
			web.securityInterceptor(securityInterceptor);
		});
	}
	

里面的核心方法其实就是 WebSecurityConfigurerAdapter#getHttp,下面我们就来具体分析一下这个方法:

WebSecurityConfigurerAdapter#getHttp

	protected final HttpSecurity getHttp() throws Exception {
		if (http != null) {
			return http;
		}

		DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
				.postProcess(new DefaultAuthenticationEventPublisher());
		localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

		AuthenticationManager authenticationManager = authenticationManager();
		authenticationBuilder.parentAuthenticationManager(authenticationManager);
		authenticationBuilder.authenticationEventPublisher(eventPublisher);
		Map<Class<?>, Object> sharedObjects = createSharedObjects();

		http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
				sharedObjects);
		if (!disableDefaults) {
			// @formatter:off
			http
				.csrf().and()
				.addFilter(new WebAsyncManagerIntegrationFilter())
				.exceptionHandling().and()
				.headers().and()
				.sessionManagement().and()
				.securityContext().and()
				.requestCache().and()
				.anonymous().and()
				.servletApi().and()
				.apply(new DefaultLoginPageConfigurer<>()).and()
				.logout();
			// @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;
	}

这个方法看着挺复杂的我们就来理一下它的方法实现的具体功能:

  • 首先第一步创建 DefaultAuthenticationEventPublisher 对象用于发布授权成功或者失败事件。
  • 然后创建 AuthenticationManager 对象,它的作用主要是授权管理作用。
  • 接着就是创建并设置 HttpSecurity 的值。它默认会配置 11 个过滤器。
  • 最后通过 WebSecurityConfigurerAdapter#configure 对 HttpSecurity 进行配置。

HttpSecurity#csrf() 为例,来讲解 HttpSecurity 添加过滤器的过程:

HttpSecurity#csrf()

	public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
		ApplicationContext context = getContext();
		return getOrApply(new CsrfConfigurer<>(context));
	}
  • 首先拿到 Spring 框架的内部对象 ApplicationContext,也就是 bean 管理对象。
  • 接口创建 CsrfConfigurer 对象并调用 HttpSecurity#getOrApply 方法

HttpSecurity#getOrApply

	private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
			C configurer) throws Exception {
		C existingConfig = (C) getConfigurer(configurer.getClass());
		if (existingConfig != null) {
			return existingConfig;
		}
		return apply(configurer);
	}
  • 首先判断是否之前创建配置这个对象,如果有则直接返回
  • 然后调用 AbstractConfiguredSecurityBuilder#apply(C)进行配置

AbstractConfiguredSecurityBuilder#apply(C)

	public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
			throws Exception {
		configurer.addObjectPostProcessor(objectPostProcessor);
		configurer.setBuilder((B) this);
		add(configurer);
		return configurer;
	}
  • 为当前 SecurityConfigurerAdapter 对象也就是CsrfConfigurer添加 ObjectPostProcessor 主要用于依赖注入操作
  • 设置当前 SecurityConfigurerAdapter#securityBuilder 的属性是当前对象也就是 HttpSerurity 用于 HttpSecurity 的链式调用。通过 SecurityConfigurerAdapter#and 方法就可以获取到 HttpSecurity 对象。
  • 最后通过AbstractConfiguredSecurityBuilder#add把 CsrfConfigurer 对象添加到 AbstractConfiguredSecurityBuilder#configurers

下面就是 HttpSecurity 默认添加的 11 个 Filter。

HttpSecurity中的方法 对应的 SecurityConfigurer 对应的 Filter
HttpSecurity#csrf() CsrfConfigurer CsrfFilter
HttpSecurity#addFilter ~ WebAsyncManagerIntegrationFilter
HttpSecurity#exceptionHandling ExceptionHandlingConfigurer ExceptionTranslationFilter
HttpSecurity#headers HeadersConfigurer HeaderWriterFilter
HttpSecurity#sessionManagement SessionManagementConfigurer SessionManagementFilter
HttpSecurity#securityContext SecurityContextConfigurer SecurityContextPersistenceFilter
HttpSecurity#requestCache RequestCacheConfigurer RequestCacheAwareFilter
HttpSecurity#anonymous AnonymousConfigurer AnonymousAuthenticationFilter
HttpSecurity#servletApi ServletApiConfigurer SecurityContextHolderAwareRequestFilter
HttpSecurity#apply DefaultLoginPageConfigurer DefaultLoginPageGeneratingFilterDefaultLogoutPageGeneratingFilter
HttpSecurity#ogout LogoutConfigurer LogoutFilter

在第二小节打印的大多过滤器都可以在上面找到。最后就是 WebSecurity 通过 WebSecurity#performBuild 方法创建FilterChainProxy

	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<>(
				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 对象其实就是 HttpSecurity,通过HttpSecurity#build 方法创建 DefaultSecurityFilterChain

HttpSecurity#performBuild

	protected DefaultSecurityFilterChain performBuild() {
		filters.sort(comparator);
		return new DefaultSecurityFilterChain(requestMatcher, filters);
	}

然后再创建 FilterChainProxy 对象,如果@EnableWebSecurity#debug标注是 true,就会创建DebugFilter打印调度信息。

6、 Spring Security 的链式调用

Spring Security 的核心实现是通过一条过滤器链来确定用户的每一个请求应该得到什么样的反馈,如下图所示:
https://img-blog.csdn.net/20160623185249858

通过 WebSecurity 创建出来的 FilterChainProxy 其实间接的继承了 Filter,可以作为真正的过滤器使用。它会携带若干条过滤器链,并在承担过滤职责时,将其派发到所有过滤器链的每一过过滤器上。

FilterChainProxy#doFilter

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
		if (clearContext) {
			try {
				request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
				// 派发到过滤器链上
				doFilterInternal(request, response, chain);
			}
			finally {
				SecurityContextHolder.clearContext();
				request.removeAttribute(FILTER_APPLIED);
			}
		}
		else {
			doFilterInternal(request, response, chain);
		}
	}

FilterChainProxy#doFilterInternal 是真正执行虚拟过滤器链逻辑的方法

FilterChainProxy#doFilterInternal

	private void doFilterInternal(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// 附上 Spring Security 提供的 Http 防火墙
		FirewalledRequest fwRequest = firewall
				.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse fwResponse = firewall
				.getFirewalledResponse((HttpServletResponse) response);
		// 按照配置的 RequestMatcher ,决定每一个请求会经过哪些过滤器
		List<Filter> filters = getFilters(fwRequest);

		if (filters == null || filters.size() == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug(UrlUtils.buildRequestUrl(fwRequest)
						+ (filters == null ? " has no matching filters"
								: " has an empty filter list"));
			}

			fwRequest.reset();
            // 模拟过滤器的执行流程,执行整条过滤器链
			chain.doFilter(fwRequest, fwResponse);

			return;
		}

		VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
		vfc.doFilter(fwRequest, fwResponse);
	}


	private static class VirtualFilterChain implements FilterChain {
		private final FilterChain originalChain;
		private final List<Filter> additionalFilters;
		private final FirewalledRequest firewalledRequest;
		private final int size;
		private int currentPosition = 0;

		private VirtualFilterChain(FirewalledRequest firewalledRequest,
				FilterChain chain, List<Filter> additionalFilters) {
			this.originalChain = chain;
			this.additionalFilters = additionalFilters;
			this.size = additionalFilters.size();
			this.firewalledRequest = firewalledRequest;
		}

		@Override
		public void doFilter(ServletRequest request, ServletResponse response)
				throws IOException, ServletException {
			if (currentPosition == size) {
				if (logger.isDebugEnabled()) {
					logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
							+ " reached end of additional filter chain; proceeding with original chain");
				}

				// Deactivate path stripping as we exit the security filter chain
				this.firewalledRequest.reset();
				// 执行过滤器链后,调用真实的 FilterChain,完成原生过滤器的剩余逻辑
				originalChain.doFilter(request, response);
			}
			else {
				currentPosition++;

				Filter nextFilter = additionalFilters.get(currentPosition - 1);

				if (logger.isDebugEnabled()) {
					logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
							+ " at position " + currentPosition + " of " + size
							+ " in additional filter chain; firing Filter: '"
							+ nextFilter.getClass().getSimpleName() + "'");
				}
				// 通过改变下标回调的方式按照顺序执行每一个过滤器
				nextFilter.doFilter(request, response, this);
			}
		}
	}

码字不易,如果觉得这篇博客对你有用可以点赞鼓励一下。

参考:

  • 《Spring Security 实战》
发布了184 篇原创文章 · 获赞 231 · 访问量 71万+

猜你喜欢

转载自blog.csdn.net/u012410733/article/details/104598636