SpringSecurity 初始化流程源码

本篇主要讲解 SpringSecurity初始化流程的源码部分,包括核心的 springSecurityFilterChain 是如何创建的,以及在介绍哪里可以扩展个性化的配置,SpringSecurity源码其实是蛮难得 各种Builder Configure 看得真的头疼!

 1.简单介绍

 SpringSecurity 的核心功能主要包括:

 认证 (你是谁)

 授权 (你能干什么)

 攻击防护 (防止伪造身份)

 其核心就是一组过滤器链,项目启动后将会自动配置,本篇也会涉及过滤器链是如何自动初始化的。

SecurityContextPersistenceFilter 是最前面的一个filter

 请求到它时候会去检查 根据sessionId找到session 判断session 中是否存在 SecurityContext 在 则将 SecurityContext 存入当前的线程中去

 响应的时候,看当前线程是否有SecurityContext ,如果有 放入到session中去 这样不同的请求都能拿到相同的 用户认证信息。

UsernamePasswordAuthenticationFilter 该过滤器是处理表单登录的,通过表单登录提交的认证都会经过它处理

SocialAuthenticationFilter 比如这个就是社交登录使用的Filter

 详细可以看我另外一篇 SpringSocial 实现第三方QQ登录SpringSocial 实现第三方QQ登录

绿色的过滤器都是可配置的,其他颜色的都不行!

 2.SecurityAutoConfiguration

 如果是SpringBoot项目只要你依赖了SpringSecurity相关依赖依然会有自动配置类

SecurityAutoConfiguration 生效 它会导入 WebSecurityEnableConfiguration

 @EnableWebSecurity将会是我们本篇的主要切入点

 3.@EnableWebSecurity注解介绍

 该注解 它是初始化Spring Security的入口 .

 打开@EnableWebSecurity注解

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity { /**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}

 该注解类通过@Configuration和@Import配合使用引入了一个配置类(WebSecurityConfiguration)和两个ImportSelector(SpringWebMvcImportSelector,OAuth2ImportSelector),我们重点关注下WebSecurityConfiguration,它是Spring Security的核心

 4.springSecurityFilterChain初始化流程及源码

 打开WebSecurityConfiguration 它是一个配置类,主要看 springSecurityFilterChain()方法,它就是初始化

springSecurityFilterChain的核心方法

/**
* Creates the Spring Security Filter Chain
* @return the {@link Filter} that represents the security filter chain
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}

@Bean注解name属性值AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME就是XML中定义的springSecurityFilterChain

 从源码中知道过滤器通过最后的 webSecurity.build()创建,webSecurity的类型为:WebSecurity,它在 setFilterChainProxySecurityConfigurer方法中优先被创建了:

@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
} webSecurityConfigurers.sort(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;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}

 从代码中可以看到,它是直接被new出来的:

webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));

setFilterChainProxySecurityConfigurer 该方法的webSecurityConfigurers 参数是通过@Value注入的

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")

AutowiredWebSecurityConfigurersIgnoreParents的 getWebSecurityConfigurers()

 如下,就是获取所有的 WebSecurityConfigurer的类型的配置类

 而通常 我们通过继承 WebSecurityConfigurerAdapter 来自定义WebSecurityConfigurer

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;
}

再回到setFilterChainProxySecurityConfigurer方法 下面有一段这样的代码 ,对于上面获取的所有的WebSecurityConfigurer类型 循环执行 webSecurity的apply方法

for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}

webSecurity集成AbstractConfiguredSecurityBuilder 它提供apply方法 再其内部调用add方法

public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}

add(configurer),主要就是将其传入的WebSecurityConfigurer存入到 LinkedHashMap configures中,

主要代码 this.configurers.put(clazz, configs);

private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null"); 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<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}

当所有的 WebSecurityConfigurer 类型的配置 全部应用到 WebSecurity中去后 setFilterChainProxySecurityConfigurer方法也就结束了


回到创建过滤器链的方法 springSecurityFilterChain()

 它会判断我们刚刚的webSecurityConfigurers是否存在,不存在就新建一个,然后执行 webSecurity.build() 重要!

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}

最终内部会有下面这段代码, 主要关注 init() configure() 和 performBuild() 这三个方法

@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING; beforeInit();
init(); buildState = BuildState.CONFIGURING; beforeConfigure();
configure(); buildState = BuildState.BUILDING; O result = performBuild(); buildState = BuildState.BUILT; return result;
}
}

init() 内部循环遍历 所有的 WebSecurityConfigurer ,它会执行到 WebSecurityConfigurerAdapter的

private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
} for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}

configurer.init((B) this)

它只要完成两件重要的事情

初始化HttpSecurity对象(注意它和WebSecurity不一样 );

设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中;

public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}

初始化HttpSecurity对象在getHttp()方法中实现:

protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
} DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
Map<Class<?>, Object> sharedObjects = createSharedObjects(); http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}

 从代码中可以了解,HttpSecurity是直接被new出来的,在创建HttpSecurity之前,首先初始化了AuthenticationManagerBuilder对象,这里有段代码很重要就是: AuthenticationManager authenticationManager = authenticationManager();,它创建AuthenticationManager实例,打开authenticationManager()方法:

 默认实现是在 WebSecurityConfigurerAdapter 中

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}

1、个性化配置入口之configure(AuthenticationManagerBuilder auth)

 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。

如下是自己继承WebSecurityConfigurerAdapter 重写 configure(AuthenticationManagerBuilder auth),实现个性化的第一个配置入口

/**
* @author johnny
* @create 2020-01-18 下午6:40
**/
@Configuration
@Slf4j
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
log.info("【测试 定制化入口 configure(AuthenticationManagerBuilder auth) 的执行 】");
}
}

 构建完HttpSecurity实例后,默认情况下会添加默认的拦截其配置:

        http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();

 我挑一个默认的方法展开看一下比如 会话管理的sessionManagement(),内部就是去创建SessionManagementConfigurer并应用它

public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
return getOrApply(new SessionManagementConfigurer<>());
}

 getOrApply 最有一句代码 return apply(configurer);

private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}

 apply(configurer) 注意这里的 configurer传入的是SessionManagementConfigurer

public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}

最终又调用了 add(configurer); 这不过这里是给 HttpSecurity的 configurers 配置初始的,上面是配置的WebSecurity的configurers, 不要混淆,最终这些configurers会被一个个创建成 对应的过滤器Filter的 详细在后面有说明

private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null"); 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<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}

 如下图:为HttpSecurity添加了很多默认的配置

 回到 getHttp()方法

 最后调用configure(http);,这又是一个可个性化的配置入口,它的默认实现是:WebSecurityConfigurerAdapter提供的

 默认的配置是拦截所有的请求需要认证之后才能访问,如果没有认证,会自动生成一个认证表单要求输入用户名和密码。

protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}

2、个性化配置入口之configure(HttpSecurity http)

 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。

 OK,目前为止HttpSecurity已经被初始化,接下去需要设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中:

public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}

 当所有的WebSecurityConfigurer的init方法被调用之后,webSecurity.init()工作就结束了

 接下去调用了webSecurity.configure(),该方法同样是在AbstractConfiguredSecurityBuilder中实现的:

private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}

 它的主要工作是迭代调用所有WebSecurityConfigurer的configurer方法,参数是WebSeucrity本身,这又是另外一个重要的个性化入口:

3、个性化配置入口之configure(WebSecurity web)

 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity。

 至此,三个重要的个性化入口都已经被调用,即在实现WebSecurityConfigurerAdapter经常需要重写的:

1、configure(AuthenticationManagerBuilder auth);

2、configure(WebSecurity web);

3、configure(HttpSecurity http);

 回到webSecurity构建过程,接下去重要的的调用:

O result = performBuild();

performBuild() 非常重要!!

@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy;
if (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);
}
postBuildAction.run();
return result; }

 首先计算出chainSize,也就是ignoredRequests.size() + securityFilterChainBuilders.size();,如果你不配置ignoredRequests,那就是securityFilterChainBuilders.size(),也就是HttpSecurity的个数,其本质上就是你一共配置几个WebSecurityConfigurerAdapter,因为每个WebSecurityConfigurerAdapter对应一个HttpSecurity,而所谓的ignoredRequests就是FilterChainProxy的请求,默认是没有的,如果你需要条跳过某些请求不需要认证或授权,可以如下配置:

@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/statics/**");
}

 在上面配置中,所有以/statics开头请求都将被FilterChainProxy忽略。

 securityFilterChains.add(securityFilterChainBuilder.build()); 这一行就是初始化所有的过滤器,记得上面有段代码如下,将HttpSecurity设置到WebSecurity的 securityFilterChainBuilder中,上面就是调用HttpSecurity.build()方法,初始化所有的 HttpSecurity的过滤器链

public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}

 依然来到 doBuild()方法,只不过这次是执行的 HttpSecurity的

@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING; beforeInit();
init(); buildState = BuildState.CONFIGURING; beforeConfigure();
configure(); buildState = BuildState.BUILDING; O result = performBuild(); buildState = BuildState.BUILT; return result;
}
}

 重点查看 configure()该方法 会调用对应的 过滤器配置的configure()

如 再内部创建 SessionManagementFilter 最后添加到HttpSecurity中,也就是拿 HttpSecurity的configures 一个个创建出对应的过滤器

@Override
public void configure(H http) {
SecurityContextRepository securityContextRepository = http
.getSharedObject(SecurityContextRepository.class);
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
securityContextRepository, getSessionAuthenticationStrategy(http));
if (this.sessionAuthenticationErrorUrl != null) {
sessionManagementFilter.setAuthenticationFailureHandler(
new SimpleUrlAuthenticationFailureHandler(
this.sessionAuthenticationErrorUrl));
}
InvalidSessionStrategy strategy = getInvalidSessionStrategy();
if (strategy != null) {
sessionManagementFilter.setInvalidSessionStrategy(strategy);
}
AuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler();
if (failureHandler != null) {
sessionManagementFilter.setAuthenticationFailureHandler(failureHandler);
}
AuthenticationTrustResolver trustResolver = http
.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
sessionManagementFilter.setTrustResolver(trustResolver);
}
sessionManagementFilter = postProcess(sessionManagementFilter); http.addFilter(sessionManagementFilter);
if (isConcurrentSessionControlEnabled()) {
ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http); concurrentSessionFilter = postProcess(concurrentSessionFilter);
http.addFilter(concurrentSessionFilter);
}
}

 当doBuild()中的 configure();执行完毕后 的会得到如下HttpSecurity可以看到它内部的filters已经全部创建完毕

 回到doBuild()方法 该方中有 performBuild() 调用HttpSecurity的 performBuild(),默认实现如下,先对上面所有的过滤器进行排序,使用的是 FilterComparator() 进行排序的,这里不展开了,反正就是会排序成文章开始的那张图上面的顺序

@Override
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}

 最后返回的是SecurityFilterChain的默认实现DefaultSecurityFilterChain。

 构建完所有SecurityFilterChain后,创建最为重要的FilterChainProxy实例,

FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

 至此Spring Security 初始化完成,包括springSecurityFilterChain初始化,我们通过继承WebSecurityConfigurerAdapter来代达到个性化配置目的,文中提到了三个重要的个性化入口,并且WebSecurityConfigurerAdapter是可以配置多个的,其对应的接口就是会存在多个SecurityFilterChain实例,但是它们人仍然在同一个FilterChainProxy中,通过RequestMatcher来匹配并传入到对应的SecurityFilterChain中执行请求。

 5.个性化入口配置(扩展WebSecurityConfigurerAdapter)

 重要的个性化入口都是哪里调用的 已经在上面初始化 springSecurityFilterChain 源码中讲解了,这里知识总结一下

1、个性化配置入口之configure(AuthenticationManagerBuilder auth)

 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。

2、个性化配置入口之configure(HttpSecurity http)

 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。

3、个性化配置入口之configure(WebSecurity web)

 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity。

 实现WebSecurityConfigurerAdapter经常需要重写的:

1、configure(AuthenticationManagerBuilder auth);

2、configure(WebSecurity web);

3、configure(HttpSecurity http);

 6.总结

本篇主要讲解了

 1.SpringBoot对于SpringSecurity的自动配置的支持类SecurityAutoConfiguration,

 2.核心注解@EnableWebSecurity

 3. SpringSecurity的核心过滤器链 springSecurityFilterChain 的初始化流程的源码

源码部分还是定下心来多看 加油!

个人博客地址: https://www.askajohnny.com 欢迎访问!

本文由博客一文多发平台 OpenWrite 发布!

SpringSecurity 初始化流程源码的更多相关文章

  1. A2dp sink 初始化流程源码分析

    A2dp sink的初始化流程和A2dp 的初始化流程,基本一样,这里做简单分析.这里分析的android的版本是Android O. 我们先从service的启动说起吧. 下面 是启动的时候的log ...

  2. A2dp初始化流程源码分析

    蓝牙启动的时候,会涉及到各个profile 的启动.这篇文章分析一下,蓝牙中a2dp profile的初始化流程. 我们从AdapterState.java中对于USER_TURN_ON 消息的处理说 ...

  3. SpringSecurity 默认表单登录页展示流程源码

    SpringSecurity 默认表单登录页展示流程源码 本篇主要讲解 SpringSecurity提供的默认表单登录页 它是如何展示的的流程, 涉及 1.FilterSecurityIntercep ...

  4. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  5. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  6. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  7. Spring加载流程源码分析03【refresh】

      前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...

  8. Android Activity启动流程源码全解析(1)

    前言 Activity是Android四大组件的老大,我们对它的生命周期方法调用顺序都烂熟于心了,可是这些生命周期方法到底是怎么调用的呢?在启动它的时候会用到startActivty这个方法,但是这个 ...

  9. Android Activity启动流程源码全解析(2)

    接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...

随机推荐

  1. JOISC2014 Day2 E "交朋友" (思维+假的SCC)

    传送门 题目描述 你是活跃在历史幕后的一名特工,为了世界和平而夜以继日地努力着. 这个世界有N个国家,编号为1..N; 你的目的是在这N个国家之间建立尽可能多的友好关系. 你为了制定一个特工工作的计划 ...

  2. Intellij IDEA 如何使用Jrebel热部署

    My Jrebel 已经不再提供免费使用了,或使用破解版Jrebel 或可以移步至 HotSwapAgent,或使用破解的Jrebel. 一个java web项目,在写的过程中我们需要不断调试,如果没 ...

  3. linux 使用 gdb

    gdb 对于看系统内部是非常有用. 在这个级别精通调试器的使用要求对 gdb 命令有信心, 需要理解目标平台的汇编代码, 以及对应源码和优化的汇编码的能力. 调试器必须把内核作为一个应用程序来调用. ...

  4. Array数组的常用方法

    1.concat()链接数组 var a = [1,2,3]; console.log(a.concat(4,5));//[1,2,3,4,5] 2.join()分隔符链接数组变字符串,如果省略该参数 ...

  5. SVG路径无法识别问题

    SVG 路径不规范无法识别 使用 (?<=(,|-))\. 替换为0. 即可

  6. oracle中update语句修改多个字段

    如需要修改下列数据:表名为student 一般会这样写: update student set sname = '李四', sage = 20, sbirthday = to_date('2010-0 ...

  7. apache WEB服务器安装(包括虚拟主机)

    一.apache下载编译安装 yum install apr apr-devel apr-util apr-util-devel gcc-c++ wget tar -y cd /usr/src wge ...

  8. 解读中兴通信在物联网行业如何践行DDD

    此前,在由 ThoughtWorks 举办的领域驱动设计峰会 DDD-China 2019 上,InfoQ 记者就开发团队为何需要 DDD.目前业界实践 DDD 的挑战等问题对中兴通讯资深软件架构师张 ...

  9. easyUI demo2

    图片:               代码: jsp <%@ page language="java" import="java.util.*" pageE ...

  10. DOCKER学习_004:Docker网络

    一 简介 当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的docker容器会连接到这个虚拟网桥上.虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过 ...