最近在学习安全框架Spring Security,想弄清楚其中实现的具体步骤,于是下定决心,研究一下Spring Security源码,这篇博客的目的是想把学习过程记录下来。学习过程中主要参考了http://dead-knight.iteye.com/blog/1511389大神的博客,然后在其基础上,进行更详细的说明
1.Spring Security入口在是在web.xml配置了如下的过滤器,那么这个过滤器到底是怎么工作的呢
<!-- SpringSecurity需要的filter -->
<filter>
<filter-name>spring-security</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSecurityFilterChain</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>spring-security</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
我们打开DelegatingFilterProxy类,发现这个类位于org.springframework.web-3.0.1.RELEASE.jar下面,说明这个类本身是和springSecurity无关。
2.研究一下DelegatingFilterProxy类到底是干嘛的
2.1 DelegatingFilterProxy首先是调用了initFilterBean方法,初始化过滤器
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// 如果没配置targetBeanName,那么默认使用过滤器的名字
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// 获取Spring上下文,并且初始化Filter
// 如果可能的话,在Spring上下文初始化前初始化Filter,我们会使用延迟加载策略
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
// 初始化springSecurityFilterChain,详解请移步2.3
this.delegate = initDelegate(wac);
}
}
}
}
2.2 然后调用了doFilter方法,执行过滤器方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}
// 初始化springSecurityFilterChain,详解请移步2.3
this.delegate = initDelegate(wac);
}
delegateToUse = this.delegate;
}
}
// 让委托执行实际的doFilter操作
// 这里的delegateToUse实际上就是springSecurityFilterChain实例,详解请移步4
invokeDelegate(delegateToUse, request, response, filterChain);
}
2.3 接着来看看上面提到的initDelegate(wac)方法
// 初始化FilterChainProxy
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
// getTargetBeanName()返回的是Filter的name:springSecurityFilterChain
// 根据springSecurityFilterChain的bean name直接获取FilterChainProxy的实例
Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
这里大家会产生疑问,springSecurityFilterChain这个bean在哪里定义的呢?先把这个问题抛开,稍后会作出解答
3. 标签配置的初始化
在spring-security-config-4.0.3.RELEASE.jar包的META-INF目录下有2个文件spring.handlers和spring.schemas。其中spring.schemas文件主要是标签的规范,约束;而spring.handlers这个文件时真正解析自定义标签的类,这个文件的内容为:
http\://www.springframework.org/schema/security=org.springframework.security.config.SecurityNamespaceHandler
3.1 标签解析
从上面可以看出来Spring Security的标签解析由org.springframework.security.config.SecurityNamespaceHandler来处理。该类实现接口:NamespaceHandler,Spring中自定义标签都要实现该接口,这个接口有三个方法init、parse、decorate,其中init用于自定义标签的初始化,parse用于解析标签,decorate用于装饰。SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作。打开SecurityNamespaceHandler源码。
public void init() {
loadParsers();
}
private void loadParsers() {
// 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());
parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser());
// Only load the web-namespace parsers if the web classes are available
if (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {
parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
}
if (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) {
parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER, new WebSocketMessageBrokerSecurityBeanDefinitionParser());
}
}
上面可以看出SecurityNamespaceHandler类的init方法完成了标签解析类的注册工作,并且http的标签解析类注册代码为:
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
3.2 HttpSecurityBeanDefinitionParser是如何解析HTTP标签的?
@SuppressWarnings({ "unchecked" })
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
// 这里创建了listFactoryBean实例和springSecurityFilterChain实例,详解移步3.2.1
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
// 获取listFactoryBean实例
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS);
List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
// 创建过滤器链代码,详解移步3.2.2
filterChains.add(createFilterChain(element, pc));
pc.popAndRegisterContainingComponent();
return null;
}
3.2.1 registerFilterChainProxyIfNecessary分析
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
// 判断是否已经注册了FILTER_CHAIN_PROXY,若已经注册则直接返回
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
return;
}
// 注册ListFactoryBean
BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS));
// 注册FilterChainProxy
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));
// 此处为FILTER_CHAIN_PROXY取别名为springSecurityFilterChain
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
抛开前面的代码不看,由最后一行可以看出这里创建了springSecurityFilterChain实例,到这里终于和前面的挂钩起来了
3.2.2 createFilterChain分析
// 创建过滤器链的代码
private BeanReference createFilterChain(Element element, ParserContext pc) {
// 判断是否需要SpringSecurity拦截
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) {
// 如果element有子节点并且instanceofElement 添加错误信息
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());
}
final BeanReference portMapper = createPortMapper(element, pc);
final BeanReference portResolver = createPortResolver(portMapper, pc);
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders);
boolean forceAutoConfig = isDefaultHttpConfig(element);
// HttpConfigurationBuilder创建过滤器链,详情移步3.2.2.1
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager);
// AuthenticationConfigBuilder创建过滤器链,详情移步3.2.2.2
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
authenticationProviders.addAll(authBldr.getProviders());
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
// 添加过滤器到unorderedFilterChain
unorderedFilterChain.addAll(httpBldr.getFilters());
unorderedFilterChain.addAll(authBldr.getFilters());
unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
// 对过滤器链进行排序,详细分析请移步3.2.2.3
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);
}
// 创建过滤器链,详情移步3.2.2.4
return createSecurityFilterChainBean(element, pc, filterChain);
}
3.2.2.1 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);
interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
for (Element urlElt : interceptUrls) {
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));
}
}
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();
}
3.2.2 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));
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();
}
这里可以看到autoConfig属性,如果该属性为true,SpringSecurity就会自动配置好过滤器链。具体如何设置查看AuthenticationConfigBuilder的create*Filter方法即可看到,具体Create分析下一篇再细说。
3.2.2.3 对过滤器链排序分析
排序主要是由Collections.sort(unorderedFilterChain, new OrderComparator());这行代码实现的
unorderedFilterChain是OrderDecorator的List集合,打开OrderDecorator源码,查看构造函数
public OrderDecorator(BeanMetadataElement bean, SecurityFilters filterOrder) {
this.bean = bean;
this.order = filterOrder.getOrder();
}
这里我们发现它其实是根据SecurityFilters定义好的顺序进行排序的
enum SecurityFilters {
FIRST(Integer.MIN_VALUE), CHANNEL_FILTER, SECURITY_CONTEXT_FILTER, CONCURRENT_SESSION_FILTER,
/** {@link WebAsyncManagerIntegrationFilter} */
WEB_ASYNC_MANAGER_FILTER, HEADERS_FILTER, CSRF_FILTER, LOGOUT_FILTER, X509_FILTER, PRE_AUTH_FILTER, CAS_FILTER, FORM_LOGIN_FILTER, OPENID_FILTER, LOGIN_PAGE_FILTER, DIGEST_AUTH_FILTER, BASIC_AUTH_FILTER, REQUEST_CACHE_FILTER, SERVLET_API_SUPPORT_FILTER, JAAS_API_SUPPORT_FILTER, REMEMBER_ME_FILTER, ANONYMOUS_FILTER, SESSION_MANAGEMENT_FILTER, EXCEPTION_TRANSLATION_FILTER, FILTER_SECURITY_INTERCEPTOR, SWITCH_USER_FILTER, LAST(Integer.MAX_VALUE);
private static final int INTERVAL = 100;
private final int order;
private SecurityFilters() {
order = ordinal() * INTERVAL;
}
private SecurityFilters(int order) {
this.order = order;
}
public int getOrder() {
return order;
}
}
3.2.2.4 创建过滤器链分析
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);
if (StringUtils.hasText(requestMatcherRef)) {
if (StringUtils.hasText(filterChainPattern)) {
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();
String id = element.getAttribute("name");
if (!StringUtils.hasText(id)) {
id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
id = pc.getReaderContext().generateBeanName(filterChainBean);
}
}
pc.registerBeanComponent(new BeanComponentDefinition(filterChainBean, id));
return new RuntimeBeanReference(id);
}
4. 调用代理类的doFilter方法
protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
通过以上分析,对FilterChainProxy如何产生的,以及Spring Security的标签是如何解析有了大体的认识。具体标签的解析,Filter链的执行,下次再更新……