一切皆有法
源码的阅读是一件很痛苦的事情, 但也是一件很有趣的事情, 看大佬们构建的工业级的框架,
所获得的提升也蛮大的.
毕竟源码阅读四舍五入约等于大神教你玩转设计模式.
虽然源码内容会引起不适, 但结合别人的博客, 也不会很难, 反复反复反复.
so 打开idea 创建一个 security 项目, 开始跟代码, do it~;
我使用的版本是 spring boot 1.5.6
@Configuration
@EnableWebSecurity
public class SecurityBrowserConfiguration extends WebSecurityConfigurerAdapter {
// do something
}
点进去 @EnableWebSecurity
可以看到
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
// 开启 debug 模式
boolean debug() default false;
}
而 @EnableGlobalAuthentication 长这个样子
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
所以可以知道 SpringWebMvcImportSelector.class
AuthenticationConfiguration.class
WebSecurityConfiguration.class
三个类是主要做事情的类. 看名字能知道这三个类的大概用途. 最终可以发现过滤器链在 WebSecurityConfiguration.class 里创建.
// Creates the Spring Security Filter Chain
// DEFAULT_FILTER_NAME = 'springSecurityFilterChain' 也就是我们的目标
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
// do something...
}
直接从这个方法里开始看, 云里雾里. 知道了大概的流程, 但是始终不透彻. 所以我们慢慢慢慢慢来的, 把所有设计到的所有, 再都慢慢看一遍.
SpringWebMvcImportSelector.class
/**
* SpringWebMvcImportSelector-支持mvc的参数安全校验,替代了 @EnableWebMvcSecurity (已过时)注解.
* 如果在 classpath 环境中存在 mvc 的关键类 DispatcherServlet 时便会引入 WebMvcSecurityConfiguration 类
*/
class SpringWebMvcImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 判断
boolean webmvcPresent = ClassUtils.isPresent(
"org.springframework.web.servlet.DispatcherServlet",
getClass().getClassLoader());
return webmvcPresent
? new String[] {
"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" }
: new String[] {};
}
}
在 mvc 环境下, 会导入 WebMvcSecurityConfiguration 这个类, 功能如下
/**
* WebMvcConfigurerAdapter接口主要是用于配置MVC的相关功能,比如参数处理器、返回值处理器、异常处理器等等。
* 该实现类只扩展了相应的参数处理器
* 也就是增加了 @AuthenticationPrincipal 注解, 可以用它来注解 Controller 层方法的参数
* 会自动从 SecurityContext 取值, 被注解的参数必须和存在 SecurityContext 的内容是同一类型
* (SecurityContext 在过滤器执行流程里面有记载, 下次介绍, 现在把他当成一个容器就好了)
* 该注解主要是方便将校验通过的 Token 用于参数赋值, 还有一个 csrf 的参数解析(csrf 目前略掉)
*/
class WebMvcSecurityConfiguration extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private BeanResolver beanResolver;
@Override
@SuppressWarnings("deprecation")
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
authenticationPrincipalResolver.setBeanResolver(beanResolver);
argumentResolvers.add(authenticationPrincipalResolver);
// 已过时
argumentResolvers.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
// // csrf token参数
argumentResolvers.add(new CsrfTokenArgumentResolver());
}
@Bean
public RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
}
}
AuthenticationConfiguration.class
/**
* ObjectPostProcessorConfiguration 配置用于创建 AutowireBeanFactoryObjectPostProcessor 类
* AutowireBeanFactoryObjectPostProcessor 的作用
* Spring Security 的配置机制会使用到很多对象, 比如 WebSecurity, ProviderManager, 各个安全Filter等。
* 但对象的创建并不是通过bean定义的形式被容器发现和注册进入容器的。而是 new 的.
* 但对于这些并未被容器管理的对象, Spring Security 也希望它们成为一个被容器管理的 bean
* 注入相应的依赖, 执行 applyBeanPostProcessorsAfterInitialization() 可以 afterSingletonsInstantiated, destroy
* 为达成这个目标,Spring Security配置机制提供了一个工具类AutowireBeanFactoryObjectPostProcessor
*/
@Configuration
public class ObjectPostProcessorConfiguration {
@Bean
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
// 源码很简单, 限于篇幅就不贴了
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}
// 例子
// objectObjectPostProcessor.postProcess(new DemoClass());
// 等于把 new DemoClass() 注入到了 IOC 容器
该配置类主要是定义或者搜集构建AuthenticationManager的构建器AuthenticationManagerBuilder所需要的一些bean,然后设置到AuthenticationManagerBuilder用于最终AuthenticationManager对象的构建。
WebSecurityConfiguration.class
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
// 这也就是我们一开始说的创建过滤器链的方法
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
// 如果没有配置类的话, 就导入一个默认的配置类
// 所以当我们只在pom文件导入security依赖的时候, 也会默认执行弹窗验证的原因
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter =
objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
webSecurity.apply(adapter);
}
// WebSecurity#build()会返回一个过滤器链
return webSecurity.build();
}
// 这个方法里面设置和排序 webSecurityConfigurers
// WebSecurityConfigurerAdapter 继承 WebSecurityConfigurer,
// 这里就是在扫描我们写的配置类, 并且按照 @Order 排序之后, 依次装入WebSecurity里面, 用于webSecurity.build();
// 看下面代码知道, 我们的配置类可以写很多个, 但是 @Order 设置的大小不能重复
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
// 创建并初始化 webSecurity
webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
// 使用 AnnotationAwareOrderComparator规则, 对所有的 webSecurityConfigurer 进行排序,
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
// 如果有两个类排序值相同, 就报错....
// 不知道为什么这样设计, 可能是过滤器链匹配路径的问题
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
}
previousOrder = order;
previousConfig = config;
}
// 依次将他加入到 webSecurity 里面
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
// 设置 webSecurityConfigurers
this.webSecurityConfigurers = webSecurityConfigurers;
}
// 上面的方法里参数设置值的时候用 spel 调用此方法, 将返回值设置进参数里面
@Bean
public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
}
// 这个类的作用就是发现所有 webSecurityConfigurer 配置类
final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
public AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
}
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType =
beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
我们目前可以知道
springSecurityFilterChain
的创建是由WebSecurityConfiguration
类检索了BeanFactory里面所有webSecurityConfigurer
类型的配置类, 并将这些配置类排序好, 装载进被初始化好的webSecurity
实例里面, 然后执行webSecurity#build()
方法创建的.
那么问题来了, build() 做了些啥事情呢?
SecurityBuilder
/*
继承树如下 :
SecurityBuilder
|-> AbstractSecurityBuilder
|-> AbstractConfiguredSecurityBuilder>
|-> AuthenticationManagerBuilder 下次讲
|-> HttpSecurity 注意这个, 应该不陌生吧
|-> WebSecurity 我们的目标啦
*/
// 看一下实际的代码
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*/
O build() throws Exception;
}
// 实际作用可以看出来就是返回一个 O 类型的`单例`对象
// 子类继承实现 doBuild(), 做实际的 Build 逻辑
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
protected abstract O doBuild() throws Exception;
}
/**
* 这个类有点复杂, 因为3个重点类都复用了它, 简述如下
* 1. 允许将多个安全配置器 SecurityConfigurer 应用到该 SecurityBuilder 上;
* 2. 定义了构建过程的生命周期(参考生命周期状态定义 BuildState );
* 3. 在生命周期基础之上实现并 final 了基类定义的抽象方法 #doBuild, 将构建划分为三个主要阶段#init,#configure,#performBuild;
* 1. 对 #init/#configure阶段提供了实现;
* 2. 对 #init/#configure阶段提供了前置回调 #beforeInit/#beforeConfigure 空方法供基类扩展;
* 3. #performBuild 定义为抽象方法要求子类提供实现;
* 4. 登记安全构建器工作过程中需要共享使用的一些对象。
*
* @param The object that this builder returns
* @param The type of this builder (that is returned by the base class)
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
// 包含了所要应用到当前 SecurityBuilder 上的所有的 SecurityConfigurer
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
// 用于记录在初始化期间添加进来的 SecurityConfigurer
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
// 存放共享对象
private final Map<Class<? extends Object>, Object> sharedObjects = new HashMap<>();
// 对象后置处理器,也就是先前介绍的那个 new AutowireBeanFactoryObjectPostProcessor()
private ObjectPostProcessor<Object> objectPostProcessor;
// 生命周期状态定义
private BuildState buildState = BuildState.UNBUILT;
// build() 保证对象是单例的
// doBuild() 控制创建对象的4个阶段, 其中
// #init() 方法, 遍历 configurers 类里面所有的配置对象, 并执行他们的 init(B builder) 方法
// #configure() 方法, 遍历 configurers 类里面所有的配置对象, 并执行他们的 configure(B builder) 方法
// performBuild() 子类覆盖重写, 用于实际控制 B.build() 返回的对象
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit(); // 留给子类拓展
init(); // 调用 configurers 里面每一个 configurer 的 init 方法
buildState = BuildState.CONFIGURING;
beforeConfigure(); // 留给子类拓展
configure(); // 调用 configurers 里面每一个 configurer 的 configure 方法
buildState = BuildState.BUILDING;
O result = performBuild(); // 子类提供实现
buildState = BuildState.BUILT;
return result;
}
}
// 应用一个 SecurityConfigurer 到该 SecurityBuilder 上
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
// 添加 SecurityConfigurer 到当前 SecurityBuilder 上,添加过程做了同步处理
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
}
/**
* 初始化B, 且配置B的相关属性, 这句话能概括它的全部特性
* B SecurityBuilder的子类
* O B.build()返回的object类型
*/
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
// 初始化 SecurityBuilder 只创建设置了共享的变量,不会设置 configure() 中需要的特殊属性
void init(B builder) throws Exception;
// 设置SecurityBuilder的特殊属性
void configure(B builder) throws Exception;
}
/*
继承树如下 :
SecurityConfigurer>
|-> WebSecurityConfigurer> extends SecurityConfigurer // 这是一个空接口
|-> WebSecurityConfigurerAdapter implements WebSecurityConfigurer (是不是感觉终于见到老朋友了)
*/
// 注意看它的 init() 和 configure() 方法
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
// 它作为 SecurityConfigurer 所具有的两个功能
// 1, 只设置了共享的变量 securityInterceptor
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
// 这个注册的线程在 WebSecurity#performBuild() 里面, 被调用, 但是是直接run()的, 而不是start().
// 用于设置 securityInterceptor
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
// 2, 可以配置需要忽略的请求
public void configure(WebSecurity web) throws Exception {
}
/**
* 其中 getHttp() 用于获取HttpSecurity实例, 它长这个样子
* 我们继承这个类, 重写了 configure(http) 方法的时候, 会在 httpSecurity.configurers 里加入 filter
* http.csrf() 会将 CsrfConfigurer 存入 HttpSecurity.configurers 中
* http.csrf().disable() 从 HttpSecurity.configurers 中移除 CsrfConfigurer
* 依次类推
*/
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
// 事件发布器
DefaultAuthenticationEventPublisher eventPublisher =
objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
// 会调用 configure(AuthenticationManagerBuilder auth)
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
// 共享对象
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
// 初始化 httpSecurity 对象
http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects);
if (!disableDefaults) {
// headers()等方法将configure apply()到了http的属性configurers中,这里默认会注入10个configurer
http.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
.logout();
ClassLoader classLoader = this.context.getClassLoader();
// 默认是为空
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
// this.configurer(http) 根据this的实现类选择对应方法
configure(http);
return http;
}
}
// 子类一般继承实现的3个configure()调用时间全都看到了
SecurityFilterChain
FilterChainProxy
// 通过build()获取到实例后,存入FilterChainProxy的属性List filterChains中,
// FilterChainProxy.doFilterInternal()中执行getFilters(HttpServletRequest request)
// 找出对应request的SecurityFilterChain并返回Filters
public interface SecurityFilterChain {
// 1, 请求能否被匹配
boolean matches(HttpServletRequest request);
// 2, 返回此过滤器链的全部过滤器
List<Filter> getFilters();
}
// 本质上还是一个Filter, 这也是最终被反回的对象的类型
public class FilterChainProxy extends GenericFilterBean {
// 重要属性,由WebSecurity中的performBuild()方法传递值过来
private List<SecurityFilterChain> filterChains;
// somethings
}
先小结一下:
// 看这个声明, 可以知道 webSecurity#build(), 可以作为一个 SecurityBuilder 创建一个 Filter
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>
// httpSecurity#build(), 可以创建一个 DefaultSecurityFilterChain
public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity>
这是一个典型的建造者模式, 还有一个没有介绍的 AuthenticationManagerBuilder(在AuthenticationConfiguration.class里面).
WebConfiguration 控制 webSecurity 的创建, 而 webSecurity 又控制 httpSecurity 的创建过程.
FilterChainProxy由WebSeurity.Class负责构建,在WebSeurity.Class构建过程中,同时会对HttpSecurity进行构建,由HttpSecurity构建出内部的Filter拦截链
目前该知道的都知道了, 接下来详细讲 webSecurity#build() 是怎么创建一个 filter 的
- 从 WebSecurityConfiguration 类的 setFilterChainProxySecurityConfigurer() 方法说起;
- 在参数上的 sqel 表达式, 收集到应用环境里面所有的 WebSecurityConfigurer,
- 将他设置进 SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurers 参数里面
- new 一个 webSecurity 对象, 并用 objectPostProcessor 处理, 让他具有和被ioc持有的bean一样的性质
- 将 webSecurityConfigurers 进行排序, 如果排序值相同就报错
- 依次将 webSecurityConfigurers 里面的每一个 WebSecurityConfigurer 设置进 WebSecurity 里面;
- 将 WebSecurityConfigurer 设置进 WebSecurity 调用了它的 apply() 方法
- 也就是将 WebSecurityConfigurer 设置到 AbstractConfiguredSecurityBuilder 的 configurers 里面
- configurers 的类型是 LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>
- 最后将 WebSecurityConfiguration 类的 webSecurityConfigurers 属性赋值
- springSecurityFilterChain() 返回一个 'springSecurityFilterChain'
- 查看 WebSecurityConfiguration 类的 webSecurityConfigurers 是否有值
- 没有的话, 就向 WebSecurity 里 apply() 一个默认的 WebSecurityConfigurer
- 执行 webSecurity 的 build() 方法
- 执行 doBuild() 保证对象是单例的
- 执行 init() 方法
- 执行 configurers 里每一个 configurer 的 init(B builder) 方法
- configurer#init(B builder) 方法传入的就是在上面创建的WebSecurity对象
- WebSecurityConfigurerAdapter#init(WebSecurity web) 将会用 getHttp() 初始化一个 httpSecurity 对象
- 也就是 WebSecurityConfigurer 持有配置了一个 HttpSecurity 对象(上面建造者设计模式的图)
- WebSecurityConfigurerAdapter 将各式各样的 FilterConfigurer 注入到 HttpSecurity
- HttpSecurity 将 filter 用 FilterConfigurer 配置
- 然后把HttpSecurity注入到WebSecurity中
- 并把 httpSecurity 对象 设置进 web(也就是前面创建的webSecurity)的 securityFilterChainBuilders 属性里面
- 获取 httpSecurity 里 FilterSecurityInterceptor 类型的共享对象, 设置进webSecurity里面
- 执行 configure() 方法
- 执行 configurers 里每一个 configurer 的 configure(B builder) 方法
- WebSecurityConfigurerAdapter#configure(WebSecurity web) 默认空方法, 一般用于设置需要忽略的请求
- 也就是往 ignoredRequests 里面加 RequestMatcher
- 执行 performBuild() 方法
- 创建一个 securityFilterChains, 类型是 List<SecurityFilterChain>
- 遍历 webSecurity 的 ignoredRequests, 创建匹配不处理路径的 SecurityFilterChain, 将他加入 securityFilterChains
- 遍历 securityFilterChainBuilders(也就是所有httpSecurity对象), 执行httpSecurit的build(), 将返回的值加入 securityFilterChains
- httpSecurity#build(), 这个方法返回一个过滤器链 DefaultSecurityFilterChain(requestMatcher, filters)
- requestMatcher 是匹配规则, 默认的 AnyRequestMatcher.INSTANCE;
- filters 是一组利用http.and()加入的过滤器 List<Filter> filters = new ArrayList<Filter>();
- 用 securityFilterChains 创建一个 FilterChainProxy 对象
- 设置 filterChainProxy 的一些属性, 转型为 Filter, 并将它返回 (这也就是我们的目标 'springSecurityFilterChain' )
// 吐槽下 md 对于 <> 标签会默认解析掉, 必须框在代码片段里面, 不然你们就看不到泛型了..
springboot情操陶冶-web配置(八)
Spring Security Config : 工具类 AutowireBeanFactoryObjectPostProcessor
Spring Security源码解析(二.创建FilterChainProxy)
Spring Security Config : AuthenticationConfiguration
Spring Security源码解析(一.基础知识点与流程介绍)
Spring Security源码解析(二.创建FilterChainProxy)