最近在学习安全框架Spring Security,想弄清楚其中实现的具体步骤,于是下定决心,研究一下Spring Security源码,这篇博客的目的是想把学习过程记录下来。学习过程中主要参考了http://dead-knight.iteye.com/category/220917大神的博客,然后在其基础上,进行更详细的说明
在FilterChainProxy初始化的过程中,大概描述了标签解析的一些步骤,但是还不够详细,该篇博客将进行更细致的说明。HTTP标签的解析由HTTP标签解析类org.springframework.security.config.http.HttpSecurityBeanDefinitionParser完成
1.解析过程
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
// 创建了listFactoryBean实例和springSecurityFilterChain实例
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
// 获取listFactoryBean实例
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS);
List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
// 创建过滤器链 移步2
filterChains.add(createFilterChain(element, pc));
pc.popAndRegisterContainingComponent();
return null;
}
2.创建过滤器链
private BeanReference createFilterChain(Element element, ParserContext pc) {
// 判断是否需要Security拦截
boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED));
if (!secured) {
// 如果没配置pattern属性并且配置了request-matcher-ref为空 添加错误信息
if (!StringUtils.hasText(element.getAttribute(ATT_PATH_PATTERN)) && !StringUtils.hasText(ATT_REQUEST_MATCHER_REF)) {
pc.getReaderContext().error("The '" + ATT_SECURED + "' attribute must be used in combination with" + " the '" + ATT_PATH_PATTERN + "' or '" + ATT_REQUEST_MATCHER_REF + "' attributes.", pc.extractSource(element));
}
for (int n = 0; n < element.getChildNodes().getLength(); n++) {
// 如果有子节点则添加错误信息
if (element.getChildNodes().item(n) instanceof Element) {
pc.getReaderContext().error("If you are using <http> to define an unsecured pattern, " + "it cannot contain child elements.", pc.extractSource(element));
}
}
// 创建过滤器链
return createSecurityFilterChainBean(element, pc, Collections.emptyList());
}
// portMapper、portResolver主要提供给SSL相关类使用
final BeanReference portMapper = createPortMapper(element, pc);
final BeanReference portResolver = createPortResolver(portMapper, pc);
// 新建一个空的authenticationProviders集合
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
// 通过空的authenticationProviders集合产生一个AuthenticationManager的bean定义
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders);
// 是否全采用默认配置
boolean forceAutoConfig = isDefaultHttpConfig(element);
// 详解移步3
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
// 详解移步4
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
// 配置logoutHandlers
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
// 向AuthenticationProviders中添加provider
authenticationProviders.addAll(authBldr.getProviders());
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
// 向FilterChain链中添加filters
unorderedFilterChain.addAll(httpBldr.getFilters());
unorderedFilterChain.addAll(authBldr.getFilters());
// 添加自定义的Filter,也就是custom-filter标签定义的Filter
unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
// 对过滤器进行排序
Collections.sort(unorderedFilterChain, new OrderComparator());
// 校验过滤器是否有效
checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
// The list of filter beans
List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();
for (OrderDecorator od : unorderedFilterChain) {
filterChain.add(od.bean);
}
// 创建SecurityFilterChain 详情移步5
return createSecurityFilterChainBean(element, pc, filterChain);
}
3.HttpConfigurationBuilder
public HttpConfigurationBuilder(Element element, boolean addAllAuth, ParserContext pc, BeanReference portMapper, BeanReference portResolver, BeanReference authenticationManager) {
this.httpElt = element;
this.addAllAuth = addAllAuth;
this.pc = pc;
this.portMapper = portMapper;
this.portResolver = portResolver;
this.matcherType = MatcherType.fromElement(element);
// 获取子标签intercept-url
interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
for (Element urlElt : interceptUrls) {
// 判断子标签intercept-url是否配置了filters属性
// 如果配置了filters属性添加错误消息,因为Security已经不再支持filters属性了
if (StringUtils.hasText(urlElt.getAttribute(ATT_FILTERS))) {
pc.getReaderContext().error("The use of \"filters='none'\" is no longer supported. Please define a" + " separate <http> element for the pattern you want to exclude and use the attribute" + " \"security='none'\".", pc.extractSource(urlElt));
}
}
// 获取标签create-session属性
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (StringUtils.hasText(createSession)) {
sessionPolicy = createPolicy(createSession);
} else {
// 默认策略
sessionPolicy = SessionCreationPolicy.IF_REQUIRED;
}
// 创建一系列过滤器
createCsrfFilter();
createSecurityContextPersistenceFilter();
createSessionManagementFilters();
createWebAsyncManagerFilter();
createRequestCacheFilter();
createServletApiFilter(authenticationManager);
createJaasApiFilter();
createChannelProcessingFilter();
createFilterSecurityInterceptor(authenticationManager);
createAddHeadersFilter();
}
4.AuthenticationConfigBuilder
public AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc, SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager, BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
this.httpElt = element;
this.pc = pc;
this.requestCache = requestCache;
// 是否自动配置
autoConfig = forceAutoConfig | "true".equals(element.getAttribute(ATT_AUTO_CONFIG));
// 是否允许session
this.allowSessionCreation = sessionPolicy != SessionCreationPolicy.NEVER && sessionPolicy != SessionCreationPolicy.STATELESS;
this.portMapper = portMapper;
this.portResolver = portResolver;
this.csrfLogoutHandler = csrfLogoutHandler;
// 创建一系列过滤器
createAnonymousFilter();
createRememberMeFilter(authenticationManager);
createBasicFilter(authenticationManager);
createFormLoginFilter(sessionStrategy, authenticationManager);
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
createX509Filter(authenticationManager);
createJeeFilter(authenticationManager);
createLogoutFilter();
createLoginPageFilterIfNeeded();
createUserDetailsServiceFactory();
createExceptionTranslationFilter();
}
5.创建SecurityFilterChain
private BeanReference createSecurityFilterChainBean(Element element, ParserContext pc, List<?> filterChain) {
BeanMetadataElement filterChainMatcher;
String requestMatcherRef = element.getAttribute(ATT_REQUEST_MATCHER_REF);
String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);
// 实例化filterChainMatcher
if (StringUtils.hasText(requestMatcherRef)) {
if (StringUtils.hasText(filterChainPattern)) {
// 不能在配置了request-matcher-ref的同时配置pattern
pc.getReaderContext().error("You can't define a pattern and a request-matcher-ref for the " + "same filter chain", pc.extractSource(element));
}
filterChainMatcher = new RuntimeBeanReference(requestMatcherRef);
}else if (StringUtils.hasText(filterChainPattern)) {
filterChainMatcher = MatcherType.fromElement(element).createMatcher(filterChainPattern, null);
}else {
filterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);
}
BeanDefinitionBuilder filterChainBldr = BeanDefinitionBuilder.rootBeanDefinition(DefaultSecurityFilterChain.class);
filterChainBldr.addConstructorArgValue(filterChainMatcher);
filterChainBldr.addConstructorArgValue(filterChain);
BeanDefinition filterChainBean = filterChainBldr.getBeanDefinition();
// 获取http标签的name属性
String id = element.getAttribute("name");
if (!StringUtils.hasText(id)) {
id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
// 生成ID
id = pc.getReaderContext().generateBeanName(filterChainBean);
}
}
// 注册DefaultSecurityFilterChain
pc.registerBeanComponent(new BeanComponentDefinition(filterChainBean, id));
return new RuntimeBeanReference(id);
}
至此,大概HTTP标签的解析已经差不多了,虽然每个Filter的BeanDefinition创建过程还没有一一细说,但基本步骤如下:
1.通过Filter的类路径获取BeanDefinitionBuilder对象,如BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(filterClassName)
2.解析xml标签属性,再通过BeanDefinitionBuilder的addPropertyValue,addPropertyReference等方法设置Filter对应BeanDefinition的属性值,依赖bean
3.注册BeanDefinition。通过ParserContext.registerBeanComponent(new BeanComponentDefinition(BeanDefinition,beanId));完成bean的注册,还可以通过ParserContext.getRegistry().registerAlias方法注册bean的别名
实际上,标签解析就是构造BeanDefinition,然后注册到beanfactory中,而BeanDefinition就是Spring中定义bean的数据结构