spring security 中spring bean組成的過濾器鏈如何初始化的
spring security 框架的安全是基於過濾器鏈的,但此filter是被spring容器託管的.
一般我們會在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>
這個過濾器的名字必須是springSecurityFilterChain,這個是spirng 安全框架定義的,定義的地方:
org.springframework.security.config.BeanIds中,
/** External alias for FilterChainProxy bean, for use in web.xml files */
public static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain";
spring給打註釋也說的很明白了.
既然org.springframework.web.filter.DelegatingFilterProxy
此類中的初始化函式:
@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(); //==>springSecurityFilterChain web.xml配置檔案中的過濾器名字,也是所代理的spring bean } // 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); } } } }
其中,
this.targetBeanName = getFilterName();
就是我們在web.xml中配置的名字:springSecurityFilterChain.
// 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);
}
這段程式碼是從spring root context中尋找this.delegate,即代理的真實物件:/org.springframework.security.filterChainProxy
那我們就要知道spring root context 怎麼初始話這個真實物件的.既然文件說是從spring root context來尋找的,那spirng 安全的元件最好配置在spring root context中,即:(spring 還有web context呢)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:rootContextConfig/spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
我們也知道如何配置spirng 安全元件,簡化的配置如下:
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/hello" access="hasRole('ROLE_SCARVAREZ_MEMBER')" />
<security:expression-handler ref=""/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="doctor" password="doctor" authorities="ROLE_SCARVAREZ_MEMBER"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
那我們就要知道srpring是如何解析這些xml,例項化這些類的.
spirng解析這些xml的思路主要是:NamespaceHandler、BeanDefinitionParser的各種子類。
由spirng提供的檔案spirng.handlers檔案內容:
http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler
我們必須從 org.springframework.security.config.SecurityNamespaceHandler著手,其實我們直接去找
org.springframework.security.config.http.HttpSecurityBeanDefinitionParser好了,
在
<span style="font-size:18px;">public BeanDefinition parse(Element element, ParserContext pc) </span>
程式碼內有一行關鍵點:
<span style="font-size:18px;"> registerFilterChainProxyIfNecessary(pc, pc.extractSource(element)); //這裡是建立FilterChainProxy類例項的入口.</span>
<span style="font-size:18px;">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);
}
}</span>
程式碼中會向spring容器註冊org.springframework.security.filterChainProxy例項。
此類中有一個變數:
private List<SecurityFilterChain> filterChains;
這個是spring解析spirng 安全xml或預設初始話的spirng bean組成的,會被依賴注入到filterChainProxy。
會被注入哪些bean,請參看HttpSecurityBeanDefinitionParser類中的
private BeanReference createFilterChain(Element element, ParserContext pc)
方法,關鍵是呼叫的org.springframework.security.config.http.HttpConfigurationBuilder類。