Spring Security中代理過濾器是通過FilterChain如何工作的
阿新 • • 發佈:2019-02-12
我們在搭建Spring Security框架的第一步配置是在專案的web.xml新增代理過濾器,配置如下
<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,從而實現許可權的控制。讓我們來看一下DelegatingFilterProxy的doFilter方法是如何實現的。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Lazily initialize the delegate if necessary. Filter delegateToUse = this.delegate; if (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方法,讓我們來看一下invokeDelegate方法。
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
由此可見,請求在進入DelegatingFilterProxy後是實際上由delegate做的處理。那這個delegate是如何來得呢?由上面的
可以看出,是通過initDelegate方法得到的。this.delegate = initDelegate(wac);
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
由此可見,delegate是在Spring容器的一個名為getTargetBeanName方法返回的字串的bean.而這個方法的返回值就是我們在web.xml配置的<filter-name>的值springSecurityFilterChain。現在我們來探究一下這個bean的出處。
我們在spring的配置檔案中搜索,發現並沒有註冊名為springSecurityFilterChain的bean,可以得出這個bean不是我們通過配置得到的,而是從程式碼層面實現的。
所以我們唯一的切入點就是為Spring的配置檔案新增的Spring Security新增的xsd了。對Spring配置檔案的解析都是通過BeanDefinitionParser來實現的,所以我們發現了一個叫做HttpSecurityBeanDefinitionParser的一個類。
@SuppressWarnings({"unchecked"})
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
//此方法就是用來將註冊名為springSecurityFilterChain的bean.
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;
}
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));
//此段程式碼註冊一個類進入到Spring的容器中
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
這個類就是Spring Security進行過濾鏈初始化配置的的核心類。其中createFilterChain方法就是對Security配置檔案解析以及自定義過濾器、預設過濾器進行新增的核心方法。