好久沒有更新博客了,最近對Spring Security作了比較深刻的研究。
spring security的教程網上不少:
http://lengyun3566.iteye.com/category/153689
http://wenku.baidu.com/view/b0c0dc0b79563c1ec5da7179.html
以上教程足夠應付在實際項目中使用spring security這一安全框架了。若是想深刻研究下,網上的資料就不多了,好比:
http://www.blogjava.net/SpartaYew/archive/2011/05/19/spingsecurity3.html
http://www.blogjava.net/youxia/archive/2008/12/07/244883.html
http://www.cnblogs.com/hzhuxin/archive/2011/12/19/2293730.html
但仍是沒有從filter配置開始進行一步一步分析。
帶着不少疑問,逐步撥開spring security3的面紗
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>html
一看就知道,這是spring的類,這個類位於org.springframework.web-3.0.1.RELEASE.jar這個jar下面,說明這個類自己是和springSecurity無關。DelegatingFilterProxy類繼承於抽象類GenericFilterBean,間接地實現了javax.servlet.Filter接口。細節方面就不一一講述了。看doFilter方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {java
// 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;
}web
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}spring
這裏作了兩件事:
1、initDelegate(wac);//初始化FilterChainProxy
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}安全
getTargetBeanName()返回的是Filter的name:springSecurityFilterChain
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
這裏根據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>app
這是最簡單的配置了,同時也是解開springSecurityFilterChain這個bean沒有定義的疑問了。
這裏主要利用了spring的自定義標籤。具體參見:
[url] http://www.w3school.com.cn/schema/schema_example.asp[/url]
首先spring security的標籤解析部分的源碼包爲:
spring-security-config-3.0.2.RELEASE.jar
這個jar包的META-INF目錄下面有spring.handlers,spring.schemas兩個文件,其中spring.schemas文件主要是標籤的規範、約束;而spring.handlers這個文件時真正解析自定義標籤的類,這個文件的內容爲:
http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler框架
從這裏能夠看出來spring security的標籤解析由org.springframework.security.config.SecurityNamespaceHandler
來處理。該類實現接口:NamespaceHandler,spring中自定義標籤都要實現該接口,這個接口有三個方法init、parse、decorate,其中init用於自定義標籤的初始化,parse用於解析標籤,decorate用於裝飾。
SecurityNamespaceHandler類的init方法完成了標籤解析類的註冊工做
public void init() {
// 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());
// registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());ide
// Only load the web-namespace parsers if the web classes are available
if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) {
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
//registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
}
}ui
能夠看出,http的標籤解析類註冊代碼爲:
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());this
authentication-manager的標籤解析類註冊代碼爲:
parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
HttpSecurityBeanDefinitionParser的parse方法源碼爲:
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
final Object source = pc.extractSource(element);
final String portMapperName = createPortMapper(element, pc);
final UrlMatcher matcher = createUrlMatcher(element);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher, portMapperName);
httpBldr.parseInterceptUrlsForEmptyFilterChains();
httpBldr.createSecurityContextPersistenceFilter();
httpBldr.createSessionManagementFilters();
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);
httpBldr.createServletApiFilter();
httpBldr.createChannelProcessingFilter();
httpBldr.createFilterSecurityInterceptor(authenticationManager);
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,
httpBldr.isAllowSessionCreation(), portMapperName);
authBldr.createAnonymousFilter();
authBldr.createRememberMeFilter(authenticationManager);
authBldr.createRequestCache();
authBldr.createBasicFilter(authenticationManager);
authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
authBldr.createX509Filter(authenticationManager);
authBldr.createLogoutFilter();
authBldr.createLoginPageFilterIfNeeded();
authBldr.createUserServiceInjector();
authBldr.createExceptionTranslationFilter();
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
unorderedFilterChain.addAll(httpBldr.getFilters());
unorderedFilterChain.addAll(authBldr.getFilters());
authenticationProviders.addAll(authBldr.getProviders());
BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
Collections.sort(unorderedFilterChain, new OrderComparator());
checkFilterChainOrder(unorderedFilterChain, pc, source);
List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();
for (OrderDecorator od : unorderedFilterChain) {
filterChain.add(od.bean);
}
ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = httpBldr.getFilterChainMap();
BeanDefinition universalMatch = new RootBeanDefinition(String.class);
universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());
filterChainMap.put(universalMatch, filterChain);
registerFilterChainProxy(pc, filterChainMap, matcher, source);
pc.popAndRegisterContainingComponent();
return null;
}
不少spring security的教程都會說http標籤配置了auto-config="true"屬性,spring security就會自動配置好了過濾器鏈。可是這些過濾器是如何添加到鏈中的呢,教程沒說。
可是上面的代碼已經告訴咱們,就在這裏設置的
httpBldr.createSecurityContextPersistenceFilter();
httpBldr.createSessionManagementFilters();
httpBldr.createServletApiFilter();
httpBldr.createChannelProcessingFilter();
httpBldr.createFilterSecurityInterceptor(authenticationManager);
…………
authBldr.createAnonymousFilter();
authBldr.createRememberMeFilter(authenticationManager);
authBldr.createRequestCache();
authBldr.createBasicFilter(authenticationManager);
authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
authBldr.createX509Filter(authenticationManager);
authBldr.createLogoutFilter();
authBldr.createLoginPageFilterIfNeeded();
authBldr.createUserServiceInjector();
authBldr.createExceptionTranslationFilter();
具體create分析下一篇再細說。接下來完成Filter的排序、並添加到filterChainMap集合中
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
unorderedFilterChain.addAll(httpBldr.getFilters());
unorderedFilterChain.addAll(authBldr.getFilters());
authenticationProviders.addAll(authBldr.getProviders());
BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
Collections.sort(unorderedFilterChain, new OrderComparator());
checkFilterChainOrder(unorderedFilterChain, pc, source);
List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();
for (OrderDecorator od : unorderedFilterChain) {
filterChain.add(od.bean);
}
ManagedMap<BeanDefinition, List<BeanMetadataElement>> filterChainMap = httpBldr.getFilterChainMap();
BeanDefinition universalMatch = new RootBeanDefinition(String.class);
universalMatch.getConstructorArgumentValues().addGenericArgumentValue(matcher.getUniversalMatchPattern());
filterChainMap.put(universalMatch, filterChain);
此時,已經爲FilterChainProxy提供了必須的參數,接下來就是該完成FilterChainProxy的bean定義過程了
registerFilterChainProxy(pc, filterChainMap, matcher, source);
private void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, List<BeanMetadataElement>> filterChainMap, UrlMatcher matcher, Object source) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
pc.getReaderContext().error("Duplicate <http> element detected", source);
}
//定義FilterChainProxy的BeanDefinition構造對象
BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addPropertyValue("matcher", matcher);
fcpBldr.addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
//注入過濾器鏈
fcpBldr.addPropertyValue("filterChainMap", filterChainMap);
BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
//註冊bean
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
//註冊bean的alias,其中別名爲springSecurityFilterChain
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
這裏須要說明的是BeanDefinitionBuilder類,該類可以動態建立spring的bean,並經過ParserContext完成bean的註冊,而不須要在xml中進行配置。
此時FilterChainProxy實例化過程已經完成。
2、invokeDelegate(delegateToUse, request, response, filterChain);
//調用代理類的doFilter方法
protected void invokeDelegate(
Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
執行第一步獲取的FilterChainProxy實例的doFilter方法。 經過以上分析,對FilterChainProxy如何產生的,以及Spring Security的標籤是如何解析有了大致的認識。 具體標籤的解析、Filter鏈的執行.