您的位置:首页 > 其它

DelegatingFilterProxy

2018-01-31 00:05 253 查看

前言

最近可算把一个项目整完了,然后最近打算来消化下项目中不懂的地方。项目中用到了spring-security来做验证授权,这一块是自己的知识盲区。所以先就来攻克这一块。

我们都知道在使用spring-security的时候都需要在web.xml做如下配置,这样的配置就表明了spring-security实际上是通过过滤器来实现的,很显然利用Filter确实是最好的选择。那么到底是如何通过以下配置,让过滤器和spring-security联系起来的呢,我们就来探讨一下,找到执行这一块的过程,接着,我们就使用spring-security的官方文档来一边学习一边看源码。

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


DelegatingFilterProxy

这个类的作用就和他的名字一样,他就是一个起代理作用的过滤器,而真正的拦截器当然就是是spring-security里面的配置咯。但是我们知道,过滤器是由Tomcat来解析加载的,跟Spring容器是没有关系的。

所以我们就带着两个问题来看看这个类:

0.该类是如何完成代理。

1.是如何将过滤器和Spring容器结合起来的。

我们看源码:

先看父类GenericFilterBean

public abstract class GenericFilterBean implements
Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {


他的父类GenericFilterBean实现了很多接口,其中我们最熟悉的就是InitializingBean了。我们知道注入到Spring的Bean,在实例化之后,如果有实现InitializingBean接口的,会调用他的afterPropertiesSet方法。

而我们也注意到虽然GenericFilterBean是一个抽象类,但是他显式的实现了Filter的init方法,而子类DelegatingFilterProxy并没有显式的实现init方法,所以,也就是说当我们向tomcat注册了DelegatingFilterProxy过滤器后,会调用该监过滤器的init方法,而实际上调用的是父类的init方法,我们来看看这个方法做了什么。

public final void init(FilterConfig filterConfig) throws ServletException {
Assert.notNull(filterConfig, "FilterConfig must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
}

this.filterConfig = filterConfig;

// Set bean properties from init parameters.
try {
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
String msg = "Failed to set bean properties on filter '" +
filterConfig.getFilterName() + "': " + ex.getMessage();
logger.error(msg, ex);
throw new NestedServletException(msg, ex);
}

// Let subclasses do whatever initialization they like.
initFilterBean();

if (logger.isDebugEnabled()) {
logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
}
}


这个init()方法主要前面很长一段代码,直到 initFilterBean();之前,都是用来获取Filter配置中的参数即init-param value的配置。我们需要关注的就是initFilterBean();方法,这个方法在父类中是一个抽象方法,具体的实现在

@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}


我们分析下这个方法

0.首先要做一个同步操作。

1.然后进入同步块,如果当前的代理对象为null,查看targetBeanName对象是否为null,targetBeanName属性可以通过在web.xml中的filter中的init-param中配置。一般我们不显式的配置。

2.如果我们没有显式的配置的话,就调用getFilterName方法来得到targetBeanName:

protected final String getFilterName() {
return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
}


方法很简单,如果这时我们没有在filter初始化参数中配置beanName的话,我们就取当前filterName,filterName是filter配置中必须要配置的。

3.得到当前的spring上下文,因为当我们通过ContextLoaderListener这个监听器去初始化Spring容器的时候,会把根上下文当做一个attribute注入到servletContext(即servlet上下文)中,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,value为根上下文,而我们在过滤器中又能通过FIlterConfig.getServletContext去得到ServletContext,所以我们也就顺理成章的能得到spring上下文了。

4.当前上下文不为空的时候,调用initDelegate(wac);来得到真正用来执行的过滤器。

protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}


这个方法很简单,就是通过限定BeanName以及Bean的类型为Filter来获取bean,而这里的BeanName就是targetName,当没有在filter配置中配置的时候,我们直接用的是filterName,而我们这里的配置的filterName是springSecurityFilterChain。看到这里,你肯定能得出一个结论,那就是如果你不在filter里配置targetBeanName或者beanName的时候,你的filterName就必须是springSecurityFilterChain,否则,肯定就无法在Spring找到delegate对象了,而BeanName为springSecurityFilterChain的Bean对象肯定在我们配置spring-security配置的时候以默认的方式注入到了spring,只有这样才说的通。

而对于DelegatingFilterProxy的dofilter方法,我们不用多看,猜也猜得到内部是调用delegate.dofilter方法。

那文章的最后,我们就来找一下到底这个BeanName为springSecurityFilterChain的Bean对象是哪个把,也算为我们之后阅读spring-security模块的代码开个头。

我们知道spring-security的配置是如下的形式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http>
<intercept-url pattern="/**" access="hasRole('USER')"></intercept-url>
</http>
</beans>


而且会使用spring-security命名空间。很明显又是使用了命名空间来解析xml配置。这里我们不赘述整个过程了,我们直接找到解析http节点的BeanDefinitionParser实现类。

这个类就是org.springframework.security.config.http.HttpSecurityBeanDefinitionParser,我们直接看他的parse方法。

public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(
element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);

registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));

// Obtain the filter chains and add the new chain to it
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(
BeanIds.FILTER_CHAINS);
List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean
.getPropertyValues().getPropertyValue("sourceList").getValue();

filterChains.add(createFilterChain(element, pc));

pc.popAndRegisterContainingComponent();
return null;
}


这个方法的具体实现我们先不分析,我们看到他的一个子方法

registerFilterChainProxyIfNecessary

static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
return;
}
// Not already registered, so register the list of filter chains and the
// FilterChainProxy
BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean,
BeanIds.FILTER_CHAINS));

BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder
.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);
fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(
DefaultFilterChainValidator.class));
BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean,
BeanIds.FILTER_CHAIN_PROXY));
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY,
BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}

}


我们看到倒数第二行代码,将一个Bean注入到spring中,名字为BeanIds.FILTER_CHAIN_PROXY。

接着我们看到方法的最后一行代码,给这个Bean赋一个别名BeanIds.SPRING_SECURITY_FILTER_CHAIN,而对应的值就是springSecurityFilterChain

/** External alias for FilterChainProxy bean, for use in web.xml files */
public static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain";


所以这里也就完成了DelegatingFilterProxy中delegate对象注入到spring容器的过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: