spring security:DelegatingFilterProxy和FilterChainProxy之间的关系

本文主要解答的是为什么在web.xml中配置了

<filter>
     <filter-name>springSecurityFilterChain</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</fiter>
DelegatingFilterProxy代理的Filter就会是定义在spring容器中的FilterChainProxy类的bean


DelegatingFilterProxy类是一个spring类,位于org.springframework.web.jar包下,这个类本身是和spring security无关。该类继承于抽象类GenericFilterBean,间接地实现了javax.servlet.Filter接口,Servlet容器在启动时,首先会调用Filter的init方法,GenericFilterBean的作用主要是可以把Filter的初始化参数自动地set到继承与GenericFilterBean类的Filter中区。其源码如下:

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");
		}
	}
在该方法中调用了initFilterBean()方法,该方法是GenericFilterBean类特地留给子类扩展使用的。其实现在DelegatingFilterProxy中
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);
				}
			}
		}
	}
可以看出上述代码首先看Filter是否提供了targetBeanName初始化参数,如果没有提供则直接使用filter的name作为beanName,产生beanName后,由于我们在web.xml中的filter的name是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>

FilterChainProxy 从Spring IOC容器中取出bean的代码是initDelegate方法。
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
		Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
		if (isTargetFilterLifecycle()) {
			delegate.init(getFilterConfig());
		}
		return delegate;
	}

在这个方法中,getTargetBeanName()返回的是值为springSecurityFilterChain,下面的内容是关键

wac.getBean(getTargetBeanName(), Filter.class)
wac.getBean(getTargetBeanName(),Filter.class)->AbstractApplicationContext.getBean(name,requiredType)->AbstractBeanFactory中的transformedBeanName(name)->canonicalName(BeanFactoryUtils.transformedBeanName(name))
在canonicalName方法中将springSecurityFilterChain给转换成了org.springframework.security.filterChainProxy。这是因为SecuritynamespaceHandler在解析security命名空间的时候在HttpSecurityBeanDefinitionParser中的registerFilterChainProxyIfNecessary方法中调用了ParserContext.getRegistry().registerAlias("org.springframework.security.filterChainProxy","springSecurityFilterChain")将他们的对应关系写入到了SimpleAliasRegistry中的aliasMap中去了,而canonicalName方法使用springSecurityFilterChain为key去aliasMap中取,所以得到是org.springframework.security.filterChainProxy

这里根据springSecurityFilterChain的bean name直接获取FilterChainProxy的实例。可是springSecurityFilterChain这个bean在哪里定义的呢?此时似乎忽略了spring security的bean配置文件了。
<?xml version="1.0" encoding="UTF-8"?>  
    <beans:beans xmlns="http://www.springframework.org/schema/security"  
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
           xmlns:beans="http://www.springframework.org/schema/beans"  
           xsi:schemaLocation="  
          http://www.springframework.org/schema/beans  
          http://www.springframework.org/schema/beans/spring-beans.xsd  
          http://www.springframework.org/schema/security  
          http://www.springframework.org/schema/security/   
                  spring-security-3.0.xsd">  
      <http auto-config="true">  
        <intercept-url pattern="/*" access="ROLE_USER"/>  
      </http>  
      <authentication-manager alias="authenticationManager">  
        <authentication-provider>  
          <user-service>  
            <user authorities="ROLE_USER" name="guest" password="guest"/>  
          </user-service>  
        </authentication-provider>  
      </authentication-manager>   
    </beans:beans>

这是最简单的配置,同时也解开了springSecurityFilterChain这个bean没有定义的疑问。这里主要利用了spring的自定义标签。首先spring security的标签解析部分的源码包为:spring-security-config.jar中
spring security的标签解析由org.springframework.security.config.SecurityNamespaceHandler来处理。该类实现接口:NamespaceHandler,spring中自定义标签都要实现该接口,该接口有三个方法init、parse、decorate,其中init用于自定义标签的初始化,parse用于解析标签,decorate用于装饰。
SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作
public void init() {
        loadParsers();
    }

    @SuppressWarnings("deprecation")
    private void loadParsers() {
        // Parsers
        parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
        parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());
        parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
        parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
        parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
        parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
        parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
        parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());//authentication-manager的标签解析类注册
        parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());

        // Only load the web-namespace parsers if the web classes are available
        if(ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {
            parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
            parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());//http的标签解析类注册
            parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
            parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
            parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
            parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
            filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
        }
    }
HttpSecurityBeanDefinitionParser的parse方法源码为:
public BeanDefinition parse(Element element, ParserContext pc) {
        CompositeComponentDefinition compositeDef =
                new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
       ......                                                                                                                                 
	registerFilterChainProxyIfNecessary(pc,pc.extractSource(element));
	......
}
	
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
	logger.debug("\n registerFilterChainProxyIfNecessary----"+pc.getRegistry()+"\n source=="+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);
	//BeanIds.FILTER_CHAIN_PROXY->org.springframework.security.filterChainProxy
	//BeanIds.SPRING_SECURITY_FILTER_CHAIN->springSecurityFilterChain
}
这里需要说明的是BeanDefinitionBuilder类,该类能够动态创建spring的bean,并通过ParserContext完成bean的注册,而不需要在xml中进行配置。
此时FilterChainProxy实例化过程已经完成。
下面再看一下DelegatingFilterProxy类的doFilter方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
 
        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = null;
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
                }
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
 
        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }
真正要关注invokeDelegate(delegateToUse, request, response, filterChain);这句代码,在下面可以看出 DelegatingFilterProxy类实际 是用其delegate属性即org.springframework.security.FilterChainProxy实例的doFilter方法来响应请求。
protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
 
        delegate.doFilter(request, response, filterChain);
    }

以上就是DelegatingFilterProxy类的一些内部运行机制,其实主要作用就是一个代理模式的应用,可以把servlet 容器中的filter同spring容器中的bean关联起来。

参考:http://www.aichengxu.com/java/1143729.htm

另外关于

Http标签的解析——HttpSecurityBeanDefinitionParser

可参考http://www.tuicool.com/articles/vU3m6r

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值