Spring源码之DelegatingFilterProxy
一、类图
DelegatingFilterProxy是一个代理类,代理我们标准的Filter类,并将其委托给一个实现了Filter接口的实例,不同之处在于这个实例是托管到spring中的。好处就很明显了,它可以享受到spring的便利之处,例如在filter类中注入其他的spring bean,更为复杂的初始化逻辑等等。
既然是托管到了spring中,那么filter的生命周期就交给了spring来管理。但是filter是有自己的生命周期的,DelegatingFilterProxy通过一个名叫targetFilterLifecycle的属性指定是否需要完成filter自己的生命周期方法。
二、属性
@Nullable
private String contextAttribute;
//springmvc 上下文
@Nullable
private WebApplicationContext webApplicationContext;
// 代理的filter的全名称,springmvc上下文通过此名称找到容器中的Filter赋值给delegate
@Nullable
private String targetBeanName;
private boolean targetFilterLifecycle = false;
//代理的filter
@Nullable
private volatile Filter delegate;
private final Object delegateMonitor = new Object();
三、重要方法
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// 如果没有设置targetBeanName,那么就使用filter的name当作targetBeanName
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// 通过springmvc容器获得代理的filter
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
// 获得代理的filter的方法
this.delegate = initDelegate(wac);
}
}
}
}
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
核心方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 线程安全的懒汉式
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
// 获取代理的filter类
delegateToUse = initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
// 执行代理filter类的doFilter方法
invokeDelegate(delegateToUse, request, response, filterChain);
}
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
四、SpringSecurity中的应用
一般会在web.xml配置,这个配置的作用就是使用DelegatingFilterProxy去代理springSecurityFilterChain的过滤器,所以过滤器名称必须叫springSecurityFilterChain,以方便在spring容器中找到这个过滤器。
<!--SpringSecurity过滤器链,注意过滤器名称必须叫springSecurityFilterChain-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
五、Springboot中DelegatingFilterProxy怎么自动注入的?
- 在springboot自动配置jar包里面有一个org.springframework.boot.autoconfigure.security.servlet的自动配置包。它主要负责去导入一些security需要的配置。
我们首先看SecurityAutoConfiguration这个自动配置类:
@Configuration
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
// 这边会导入一些配置类,我们先看WebSecurityEnablerConfiguration
@Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
public SecurityAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean({AuthenticationEventPublisher.class})
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
下面这个配置类有一个@EnableWebSecurity的注解,这个注解主要是开启websecurity的自动配置。
@Configuration
@ConditionalOnBean({WebSecurityConfigurerAdapter.class})
@ConditionalOnMissingBean(
name = {"springSecurityFilterChain"}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
public WebSecurityEnablerConfiguration() {
}
}
来让我们看一下@EnableWebSecurity
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
// 这个注解也注入了一些配置类,我们主要看WebSecurityConfiguration
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
其中WebSecurityConfiguration就定义了springSecurityFilterChain是怎么注入容器中的。
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
@Autowired(
required = false
)
private ObjectPostProcessor<Object> objectObjectPostProcessor;
public WebSecurityConfiguration() {
}
@Bean
public static DelegatingApplicationListener delegatingApplicationListener() {
return new DelegatingApplicationListener();
}
@Bean
@DependsOn({"springSecurityFilterChain"})
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
return this.webSecurity.getExpressionHandler();
}
// 这个方法就会向容器中注入springSecurityFilterChain的filter类。
@Bean(
name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
// 通WebSecurity的build方法创建springSecurityFilterChain过滤器
return (Filter)this.webSecurity.build();
}
......
我们重点看webSecurity.build()方法怎么玩的?
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
// 通过doBuild方法得到
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
// 继续追踪,跳到了AbstractConfiguredSecurityBuilder类里面
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init(); // 会遍历所有的 WebSecurityConfigurerAdapter ,并执行其 init 方法。
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure(); // configure 方法会遍历所有的 WebSecurityConfigurerAdapter ,并执行其 configure 方法。
buildState = BuildState.BUILDING;
// 调用了performBuild方法得到
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
// 继续追踪,我们使用WebSecurity的实现方法,跳到了WebSecurity类
@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();
// 这里将SecurityFilterChain过滤链集合给定义好
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());
}
// 这边可以看到返回的springSecurityFilterChain就是new的FilterChainProxy,并将SecurityFilterChain过滤链集合传过去。
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;
}
当容器中有上面的springSecurityFilterChain类后,就将DelegatingFilterProxy也注入了进去,并设置targetBeanName为springSecurityFilterChain。
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({SecurityProperties.class})
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
@AutoConfigureAfter({SecurityAutoConfiguration.class})
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
public SecurityFilterAutoConfiguration() {
}
@Bean
@ConditionalOnBean(
name = {"springSecurityFilterChain"}
)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));
return registration;
}
那么springSecurityFilterChain究竟是什么类?
上面我们知道springSecurityFilterChain是FilterChainProxy,它是属于springsecurity的一个过滤类,和DelegatingFilterProxy一样继承了GenericFilterBean
// 有删减
public class FilterChainProxy extends GenericFilterBean {
// 保存了多个SecurityFilterChain类型的过滤器链
private List<SecurityFilterChain> filterChains;
// 核心方法
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);
//实际调用这个方法
this.doFilterInternal(request, response, chain);
} finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
} else {
this.doFilterInternal(request, response, chain);
}
}
// 实际调用的方法
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
// 获得匹配成功的过滤器链中的过滤器集合
List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
if (filters != null && filters.size() != 0) {
// 运行获得的过滤器集合
FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
} else {
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);
}
}
// 通过遍历过滤器链集合获得过滤器集合
private List<Filter> getFilters(HttpServletRequest request) {
Iterator var2 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if (!var2.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var2.next();
// 需要经过 chain.matches(request) 判断到底哪个过滤器链匹配成功,每个 request 最多只会经过一个 SecurityFilterChain。
} while(!chain.matches(request));
return chain.getFilters();
}
List<SecurityFilterChain> filterChains 到底保存了哪些filter呢?
就是常用的一些Spring Security过滤器(SpringSecurity有介绍)。