本文主要解答的是为什么在web.xml中配置了
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
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。
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
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配置文件了。
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中进行配置。
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
另外关于