SpringSecurity 源码(一)——SpringSecurity 如何被加载进来的? 前言 这篇文章还是参考了这个大佬的这篇文章 ,文笔不好,如果觉得难理解可以看大佬的文章
xml 的方式配置 SpringSecurity 在 web.xml 中添加 filter 的相关配置
1 2 3 4 5 6 7 8 9 10 <filter > //这里的filter-name必须是springSecurityFilterChain,这里先记住后面源码分析的时候可以知道为什么一定要叫这个名字 <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 >
上面在 web.xml 中声明了 DelegatingFilterProxy 这个 filter 并且在 url-pattern 中以 /*让它拦截所以的请求,那么在我们第一次请求的时候就调用 DelegatingFilterProxy 的 init 方法,阅读源码发现调用逻辑简单总结如下
可以看到在 DelegatingFilterProxy 的 initFilterBean()方法中调用了 AbstractApplicationContext 的 getBean(String name, Class< T > requiredType)方法
1 2 3 4 5 6 7 8 9 10 protected Filter initDelegate (WebApplicationContext wac) throws ServletException { String targetBeanName = getTargetBeanName(); Assert.state(targetBeanName != null , "No target bean name set" ); Filter delegate = wac.getBean(targetBeanName, Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
这个 targetBeanName 的值是在上层的 DelegatingFilterProxy 的 initFilterBean()方法中设置的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 protected void initFilterBean () throws ServletException { synchronized (this .delegateMonitor) { if (this .delegate == null ) { if (this .targetBeanName == null ) { this .targetBeanName = getFilterName(); } WebApplicationContext wac = findWebApplicationContext(); if (wac != null ) { this .delegate = initDelegate(wac); } } } }
getFilterName()方法的代码
1 2 3 4 protected String getFilterName () { return (this .filterConfig != null ? this .filterConfig.getFilterName() : this .beanName); }
那这个名字叫 springSecurityFilterChain 的 bean 到底是哪个类的实例,从 getBean 方法一路深入,调用逻辑如下
在 AbstractBeanFactory 的 doGetBean(String name, @Nullable Class< T > requiredType, @Nullable Object[] args, boolean typeCheckOnly)方法中
1 2 3 4 5 6 7 8 9 10 11 protected <T> T doGetBean ( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = transformedBeanName(name); Object beanInstance; Object sharedInstance = getSingleton(beanName); ...... }
打个断点调试会发现在 AbstractBeanFactory 的 canonicalName(String name)方法中获取到了这个所谓的规范名字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public String canonicalName (String name) { String canonicalName = name; String resolvedName; do { resolvedName = this .aliasMap.get(canonicalName); if (resolvedName != null ) { canonicalName = resolvedName; } } while (resolvedName != null ); return canonicalName; }
调试的截图如下
所以这个 springSecurityFilterChain 对应的规范名字就是 org.springframework.security.filterChainProxy
那这个 aliasMap 中的 springSecurityFilterChain 的键值对数据是在哪被保存进来的呢?经过断点调试发现是在 HttpSecurityBeanDefinitionParser 中的 registerFilterChainProxyIfNecessary(ParserContext pc, Object source) 方法将这个键值对保存进 aliasMap 中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 static void registerFilterChainProxyIfNecessary (ParserContext pc, Object source) { BeanDefinitionRegistry registry = pc.getRegistry(); if (registry.containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) { return ; } 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)); registry.registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN); ...... }
BeaIds 的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public abstract class BeanIds { ...... private static final String PREFIX = "org.springframework.security." ; public static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain" ; public static final String FILTER_CHAIN_PROXY = PREFIX + "filterChainProxy" ; public static final String FILTER_CHAINS = PREFIX + "filterChains" ; ...... }
经过上面的源码分析我们知道了这个 springSecurityFilterChain 名字对应的 bean 就是 FilterChainProxy 这个类的实例,springSecurityFilterChain 只不过是这个 bean 的别名,它的规范名字是 org.springframework.security.filterChainProxy
从调试结果上看,springSecurityFilterChain 这个 bean 确实是 org.springframework.security.FilterChainProxy 这个类的实例
再回来看看 DelegatingFilterProxy 的 doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void doFilter (ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { Filter delegateToUse = this .delegate; if (delegateToUse == null ) { synchronized (this .delegateMonitor) { delegateToUse = this .delegate; if (delegateToUse == null ) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null ) { throw new IllegalStateException ("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?" ); } delegateToUse = initDelegate(wac); } this .delegate = delegateToUse; } } invokeDelegate(delegateToUse, request, response, filterChain); }
查看 DelegatingFilterProxy 的 invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)方法的代码
1 2 3 4 5 6 protected void invokeDelegate ( Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { delegate.doFilter(request, response, filterChain); }
看下 FilterChainProxy 的 doFilter(ServletRequest request, ServletResponse response, FilterChain chain)方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean clearContext = request.getAttribute(FILTER_APPLIED) == null ; if (!clearContext) { doFilterInternal(request, response, chain); return ; } try { request.setAttribute(FILTER_APPLIED, Boolean.TRUE); doFilterInternal(request, response, chain); } catch (RequestRejectedException ex) { this .requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex); } finally { SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } }
FilterChainProxy 的 doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)方法的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private void doFilterInternal (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FirewalledRequest firewallRequest = this .firewall.getFirewalledRequest((HttpServletRequest) request); HttpServletResponse firewallResponse = this .firewall.getFirewalledResponse((HttpServletResponse) response); List<Filter> filters = getFilters(firewallRequest); if (filters == null || filters.size() == 0 ) { if (logger.isTraceEnabled()) { logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest))); } firewallRequest.reset(); chain.doFilter(firewallRequest, firewallResponse); return ; } if (logger.isDebugEnabled()) { logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest))); } VirtualFilterChain virtualFilterChain = new VirtualFilterChain (firewallRequest, chain, filters); virtualFilterChain.doFilter(firewallRequest, firewallResponse); }
FilterChainProxy 的部分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class FilterChainProxy extends GenericFilterBean { private List<SecurityFilterChain> filterChains; private List<Filter> getFilters (HttpServletRequest request) { int count = 0 ; for (SecurityFilterChain chain : this .filterChains) { if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Trying to match request against %s (%d/%d)" , chain, ++count, this .filterChains.size())); } if (chain.matches(request)) { return chain.getFilters(); } } return null ; } }
默认情况下 filterChains 只有一个实例,并且这个实例有 10 个默认的过滤器
下面的代码解析会涉及到一些 spring 的加载原理,如果觉得有点晕的话可以先不要看,等简单学过 spring 源码再看也可以。
问题来了,这里的默认的过滤器链和里面的过滤器在哪定义的?我们先看下 HttpSecurityBeanDefinitionParser 的 parse(Element element, ParserContext pc)方法的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public BeanDefinition parse (Element element, ParserContext pc) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition (element.getTagName(), pc.extractSource(element)); pc.pushContainingComponent(compositeDef); registerFilterChainProxyIfNecessary(pc, pc.extractSource(element)); BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS); List<BeanReference> filterChains = (List<BeanReference>) listFactoryBean.getPropertyValues() .getPropertyValue("sourceList" ).getValue(); filterChains.add(createFilterChain(element, pc)); pc.popAndRegisterContainingComponent(); return null ; }
看完上面的代码我们应该可以猜测到这些这个过滤器链里面的过滤器应该就在 createFilterChain 这个方法里面放进去的,那就继续深入 createFilterChain 这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private BeanReference createFilterChain (Element element, ParserContext pc) { ...... HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder (element, forceAutoConfig, pc, portMapper, portResolver, authenticationManager); AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder (element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler()); ...... List<OrderDecorator> unorderedFilterChain = new ArrayList <>(); unorderedFilterChain.addAll(httpBldr.getFilters()); unorderedFilterChain.addAll(authBldr.getFilters()); unorderedFilterChain.addAll(buildCustomFilterList(element, pc)); unorderedFilterChain.sort(new OrderComparator ()); checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element)); List<BeanMetadataElement> filterChain = new ManagedList <>(); for (OrderDecorator od : unorderedFilterChain) { filterChain.add(od.bean); } return createSecurityFilterChainBean(element, pc, filterChain); }
看下 createSecurityFilterChainBean(Element element, ParserContext pc, List<?> filterChain)方法的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private BeanReference createSecurityFilterChainBean (Element element, ParserContext pc, List<?> filterChain) { ...... 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); }
上面的方法最终会返回一个 BeanReference,这个 BeanReference 里面就有这个过滤器链在 spring 容器中的名字,到时只需要 spring 只需要根据这个名字找到创建后的过滤器链实例即可
上面的分析是不是很晕,因为这说的过滤器和过滤器链都不是真正的实例,因为这个时候 spring 还没开始创建实例只是在处理 bean 之间的引用关系,所以才会有 BeanDefinition 和 BeanReference 和其他乱七八糟的东西,不过只要简单了解 spring 的启动原理其实是不难理解的(没办法啊,涉及到 spring 东西基本的加载都和 spring 的原理有关),所以如果看不懂的话可以先去了解一下 spring 原理(不需要很深入的了解,只需要知道为什么会有 BeanDefinition 和 BeanReference 和其他杂七杂八的东西就行,因为我也只是简单的看过一点,所以写得不好请不要见怪)
servlet3.0+ 环境下 java Config 配置 SpringSecurity 自定义一个 SecurityWebApplicationInitializer 并且让其继承自 AbstractSecurityWebApplicationInitializer
1 2 public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {}
在 Spring 相关的配置类上加上@EnableWebSecurity 这个注解,我这里的 Spring 相关的配置类是 MyConfig
1 2 3 4 5 @Configuration @EnableWebSecurity @ComponentScan(value = "top.ouzhanbo.webspring",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class})},useDefaultFilters = false) public class MyConfig {}
下面简单分析一下一个简单的继承和一个注解就怎么就将 SpringSecurity 加载进来
先看下 AbstractSecurityWebApplicationInitializer 的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public abstract class AbstractSecurityWebApplicationInitializer implements WebApplicationInitializer { private static final String SERVLET_CONTEXT_PREFIX = "org.springframework.web.servlet.FrameworkServlet.CONTEXT." ; public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain" ; @Override public final void onStartup (ServletContext servletContext) { beforeSpringSecurityFilterChain(servletContext); if (this .configurationClasses != null ) { AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext (); rootAppContext.register(this .configurationClasses); servletContext.addListener(new ContextLoaderListener (rootAppContext)); } if (enableHttpSessionEventPublisher()) { servletContext.addListener("org.springframework.security.web.session.HttpSessionEventPublisher" ); } servletContext.setSessionTrackingModes(getSessionTrackingModes()); insertSpringSecurityFilterChain(servletContext); afterSpringSecurityFilterChain(servletContext); } private void insertSpringSecurityFilterChain (ServletContext servletContext) { String filterName = DEFAULT_FILTER_NAME; DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy (filterName); String contextAttribute = getWebApplicationContextAttribute(); if (contextAttribute != null ) { springSecurityFilterChain.setContextAttribute(contextAttribute); } registerFilter(servletContext, true , filterName, springSecurityFilterChain); } private void registerFilter (ServletContext servletContext, boolean insertBeforeOtherFilters, String filterName, Filter filter) { Dynamic registration = servletContext.addFilter(filterName, filter); Assert.state(registration != null , () -> "Duplicate Filter registration for '" + filterName + "'. Check to ensure the Filter is only configured once." ); registration.setAsyncSupported(isAsyncSecuritySupported()); EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes(); registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters, "/*" ); } }
上面的这种使用 javaConfig 的方式相对于使用 xml 的方式跟容易理解,这代码逻辑和 web.xml 上面配置 filter 的效果是完全一样的,这种使用 SCI 的方法我在之前这篇文章里面讲过
前面讲过在 DelegatingFilterProxy 的这个 filter 的初始化中会调用到 initFilterBean()方法并在该方法中调用了 AbstractApplicationContext 的 getBean(String name, Class< T > requiredType)方法,并且这里的 targetBeanName 就是 springSecurityFilterChain,我之前分析过,这里也差不多,我就不再说一遍了,可以自己看下源码,相对于 xml 的方式,javaConfig 的配置方式理解起来已经很简单了
1 2 3 4 5 6 7 8 9 10 11 protected Filter initDelegate (WebApplicationContext wac) throws ServletException { String targetBeanName = getTargetBeanName(); Assert.state(targetBeanName != null , "No target bean name set" ); Filter delegate = wac.getBean(targetBeanName, Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
问题来了上面的名字叫 springSecurityFilterChain 这个 bean 在哪被注册到 Spring 容器中的?其实也不难猜到是@EnableWebSecurity 这个注解在搞鬼,看下这个注解的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class }) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { boolean debug () default false ; }
WebSecurityConfiguration 的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Configuration(proxyBeanMethods = false) public class WebSecurityConfiguration implements ImportAware , BeanClassLoaderAware { ...... @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain () throws Exception { boolean hasConfigurers = this .webSecurityConfigurers != null && !this .webSecurityConfigurers.isEmpty(); boolean hasFilterChain = !this .securityFilterChains.isEmpty(); Assert.state(!(hasConfigurers && hasFilterChain), "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one." ); if (!hasConfigurers && !hasFilterChain) { WebSecurityConfigurerAdapter adapter = this .objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter () { }); this .webSecurity.apply(adapter); } for (SecurityFilterChain securityFilterChain : this .securityFilterChains) { this .webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain); for (Filter filter : securityFilterChain.getFilters()) { if (filter instanceof FilterSecurityInterceptor) { this .webSecurity.securityInterceptor((FilterSecurityInterceptor) filter); break ; } } } for (WebSecurityCustomizer customizer : this .webSecurityCustomizers) { customizer.customize(this .webSecurity); } return this .webSecurity.build(); } ...... }
要想知道 webSecurity.build()返回的是那个类的实例那就从这个 build 方法一路深入,这里就那么啰嗦一个个代码贴出来了,直接给个流程图
看下 WebSecurity 的 performBuild()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 protected Filter performBuild () throws Exception { Assert.state(!this .securityFilterChainBuilders.isEmpty(), () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. " + "Typically this is done by exposing a SecurityFilterChain bean " + "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. " + "More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly" ); int chainSize = this .ignoredRequests.size() + this .securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList <>(chainSize); for (RequestMatcher ignoredRequest : this .ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain (ignoredRequest)); } for (SecurityBuilder<? extends SecurityFilterChain > securityFilterChainBuilder : this .securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } FilterChainProxy filterChainProxy = new FilterChainProxy (securityFilterChains); if (this .httpFirewall != null ) { filterChainProxy.setFirewall(this .httpFirewall); } if (this .requestRejectedHandler != null ) { filterChainProxy.setRequestRejectedHandler(this .requestRejectedHandler); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; if (this .debugEnabled) { logger.warn("\n\n" + "********************************************************************\n" + "********** Security debugging is enabled. *************\n" + "********** This may include sensitive information. *************\n" + "********** Do not use in a production system! *************\n" + "********************************************************************\n\n" ); result = new DebugFilter (filterChainProxy); } this .postBuildAction.run(); return result; }
SpringBoot 配置 SpringSecurity 只需要在启动类上加上@EnableWebSecurity 这个注解就可以了(其实可以不加,在 WebSecurityEnablerConfiguration 这个类中引入了这个注解,而 WebSecurityEnablerConfiguration 这个类又被 SecurityAutoConfiguration 这个类通过@Import 注解引入,而 SecurityAutoConfiguration 这个类又会被 SpringBoot 自动装配进来)
1 2 3 4 5 6 7 8 9 10 11 @EnableWebSecurity @SpringBootApplication public class SpringbootSpringSecurityDemoApplication { public static void main (String[] args) { SpringApplication.run(SpringbootSpringSecurityDemoApplication.class, args); } }
那 SpringBoot 是如何将 DelegatingFilterProxy 这个 filter 加载到 servlet 容器中的?其实还是用到了 SpringBoot 的自动装配,看下 SecurityFilterAutoConfiguration 这个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(SecurityProperties.class) @ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class }) @AutoConfigureAfter(SecurityAutoConfiguration.class) public class SecurityFilterAutoConfiguration { private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME; @Bean @ConditionalOnBean(name = DEFAULT_FILTER_NAME) public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration ( SecurityProperties securityProperties) { DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean ( DEFAULT_FILTER_NAME); registration.setOrder(securityProperties.getFilter().getOrder()); registration.setDispatcherTypes(getDispatcherTypes(securityProperties)); return registration; } private EnumSet<DispatcherType> getDispatcherTypes (SecurityProperties securityProperties) { if (securityProperties.getFilter().getDispatcherTypes() == null ) { return null ; } return securityProperties.getFilter().getDispatcherTypes().stream() .map((type) -> DispatcherType.valueOf(type.name())) .collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class))); } }
看过这篇文章 的话应该就能知道 SpringBoot 在启动的过程中会获取上面的 DelegatingFilterProxyRegistrationBean 这个 Bean 并且调用它的 onStartup 方法,在它的 onStartup 方法一路深入,调用逻辑如下
AbstractFilterRegistrationBean 的 addRegistration 代码
1 2 3 4 5 protected Dynamic addRegistration (String description, ServletContext servletContext) { Filter filter = getFilter(); return servletContext.addFilter(getOrDeduceName(filter), filter); }
DelegatingFilterProxyRegistrationBean 的 getFilter 方法
1 2 3 4 5 6 7 8 9 10 11 public DelegatingFilterProxy getFilter () { return new DelegatingFilterProxy (this .targetBeanName, getWebApplicationContext()) { @Override protected void initFilterBean () throws ServletException { } }; }
FilterChainProxy 的创建和初始化和上面说得都差不多,只是 DelegatingFilterProxy 是延迟初始化,看下 DelegatingFilterProxy 的 doFilter 方法就知道了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void doFilter (ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { Filter delegateToUse = this .delegate; if (delegateToUse == null ) { synchronized (this .delegateMonitor) { delegateToUse = this .delegate; if (delegateToUse == null ) { WebApplicationContext wac = findWebApplicationContext(); if (wac == null ) { throw new IllegalStateException ("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?" ); } delegateToUse = initDelegate(wac); } this .delegate = delegateToUse; } } invokeDelegate(delegateToUse, request, response, filterChain); }
上面的英文注解以说的很清楚了,如果在 initFilterBean 方法中没有初始化就会在第一次调用 filter 的时候初始化
总结 总体来说除了 xml 的那种方式的源码阅读起来是比较难懂的,其实使用 java Config 和 SpringBoot 的方式配置 SpringSecurity 的逻辑还是很好理解的。这篇文章还,文笔不好,如果觉得难理解看这个大佬的这篇文章 ,讲得真的不错,推荐!!!