在之前的项目中使用接触过通过 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
WebSecurity 在WebSecurityConfiguration#springSecurityFilterChain
中通过 WebSecurity#build
来创建 FilterChainProxy。也就是 Spring Security 的拦截器 Filter 链。
WebSecurity
实现了 SecurityBuilder
并通过 WebSecurity#performBuild
方法创建 FilterChainProxy
。
在 WebSecurity
的父类 AbstractConfiguredSecurityBuilder#doBuild
中,它的AbstractConfiguredSecurityBuilder#init
以及 AbstractConfiguredSecurityBuilder#configure
会获取 SecurityConfigurer
来通过调用 SecurityConfigurer#init
和SecurityConfigurer#configure
对 SecurityBuilder
对象进行处理。
其实WebSecurity#performBuild
是通过SecurityBuilder#build
这个方法触发的。Spring Security 通过 Java 配置来配置 SecurityBuilder
与 SecurityConfigurer
是非常重要的两个接口。
1.5 SecurityBuilder
SecurityBuilder
是 Spring Security 定义用来 Spring Security 的对象创建接口。它只有一个接口 build
,它的核心实现类就是我们上面提到的 HttpSecurity
与 WebSecurity
。
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#init
与 SecurityConfigurer#configure
。SecurityConfigurer
接口的主要作用是用来配置 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
父类AbstractConfiguredSecurityBuilder
的AbstractConfiguredSecurityBuilder#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 |
DefaultLoginPageGeneratingFilter 、DefaultLogoutPageGeneratingFilter |
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 的核心实现是通过一条过滤器链来确定用户的每一个请求应该得到什么样的反馈,如下图所示:
通过 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 实战》