Spring Security4.0.3原始碼分析之http標籤解析
阿新 • • 發佈:2019-01-04
最近在學習安全框架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的資料結構