1. 程式人生 > >spring security 中spring bean組成的過濾器鏈如何初始化的

spring security 中spring bean組成的過濾器鏈如何初始化的

pring 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

這個代理類是入口點,那我們就進去看看如何執行的吧.如何把spring bean定義的安全過濾器鏈組裝的.

此類中的初始化函式:

@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類。