SpringSecurity简介+启动流程
springSecurity简介
springSecurity是基于过滤器链的登录验证和权限校验框架,分为启动流程和执行流程,和实际应用三部部分,本章节介绍它的启动流程。后面陆续介绍它的执行流程和在生产实际中springSecurity+jwt的应用。
springSecurity的架构图

这是笔者在学习过程中绘画的关于spring-security的类图,springSecurity采用建造者(builder)设计模式,在securityBuild中定义了build方法,通过抽象类AbstractConfiguredSecurityBuilder进行通用实现,从而执行配置类(SecurityConfigurer)中的init方法和configure方法。在启动流程中会做详细介绍。
springSecurity的启动流程
在springSecurity中是通过WebSecurityConfiguration类进行安全相关的配置。该类中重点关注如下代码
@Autowired注解标注在类上在初始化对象的时候会调用该方法一次,并注入objectPostProcessor对象。@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()表示会执行autowiredWebSecurityConfigurersIgnoreParents类中的getWebSecurityConfigurers方法,并将执行结果注入给webSecurityConfigurers参数。在代码3中对该方法做了详细介绍,请在代码二中看此方法说明,也就是将我们的配置类放到List集合中返回。
代码1
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//通过objectPostProcessor生成WebSecurity对象
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
//如果我们定义了多个配置类,可以实现Ordered接口,并重写getOrder对配置类进行排序,一般只定义一个配置类,所以不需要实现Ordered接口
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
//在定义order排序编号过程中不允许重复,负责会报错
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;
}
/**
*webSecurty是SecurityBuilder类的实现类,apply方法是将配置类放到List集合中,便于执行SecurityBuilder中的build方法,从而
*执行配置类中的init方法和configure方法,进行相关配置。apply方法请参考代码2
*/
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
apply方法实际上执行的是add方法,此时的this变量指向WebSecurity。buildState变量用四种状态:buildState.UNBUILT(初始化状态前),INITIALIZING(初始化状态),BUILDING(构建中状态),BUILT(构建完成状态),会在执行后面的doBuild方法中体现。add方法实际上是将配置类对象放到webSecurity的configurers(linkedHashMap<Class,List>)集合中,换句话说将配置类(实现了SecurityConfigurer接口)放到实现了SecurityBuilder接口中的集合中。
代码2
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
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");
}
//allowConfigurersOfSameType 未初始化默认的状态是false,代表configurers Map中是否可以存在多个相同类型的配置类,
//true代表可以,false不行,如果是false,代表Map中value集合的大小只能为1.
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
}
configs.add(configurer);
//放到configurers Map集合中
this.configurers.put(clazz, configs);
//如果对象时初始化状态,则在configurersAddedInInitializing(List<SecurityConfigurer)也进行存放,此时是
//未构建状态,应该configurersAddedInInitializing集合为空
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
getWebSecurityConfigurers方法中获取实现了WebSecurityConfigurer接口的实现类,并添加到List集合列表返回。如果有用过SpringSecurty的同学就会发现,在使用springSecurity中,我们会定义一个安全配置类,继承WebSecurityConfigurerAdapter,并重写configure方法,用来表明对那些资源做拦截,或开启corf等相关配置。
代码3
final class AutowiredWebSecurityConfigurersIgnoreParents {
@SuppressWarnings({ "rawtypes", "unchecked" })
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;
}
}
通过上述代码在webSecurity对象中已经将配置类放入到WebSecurity的List集合中。最后执行webSecurity的build方法。
代码4
@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();
}
build方法实际上执行的是doBuild方法。
代码5
public final O build() throws Exception {
//通过cas防止并发
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
在doBuild方法中对buildState进行状态的切换,分别执行配置类的init方法和configure方法。
代码6
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
//从未初始化状态切换为初始化状态
buildState = BuildState.INITIALIZING;
//空实现,钩子方法
beforeInit();
//执行配置类的init方法,请看代码7
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
在此方法中由于this变量执行webSecurity,因此configurers 只有一个配置类对象,则是我们自己定义的 实现了WebSecurityConfigurerAdapter类的实现类。接下来看WebSecurityConfigurerAdapter中的init方法。如果我们重写了init,则看重写的init方法。
代码7
private void init() throws Exception {
//得到webSecurity的配置类对象,也就是通过apply方法添加到configurers Map集合中的
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
//执行 init方法,
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
// 也就是通过apply方法添加到configurersAddedInInitializing List集合中的
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
在方法中我们重点关注getHttp()方法,该方法返回安全过滤器执行链构造器SecurityFilterChainBuilder,并添加到webSecurity对象的
securityFilterChainBuilders集合中,通过postBuildAction()方法传入函数,在后续进行调用(后面会详细说明)。
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);
});
}
此时this变量指向了我们实现的配置类,即继承了抽象类 WebSecurityConfigurerAdapter 。
代码8
protected final HttpSecurity getHttp() throws Exception {
//如果已经初始化 了HttpSecurity直接返回,HttpSecurity是关于Http相关的配置,比如对什么资源做拦截等,它实现了SecurityBuilder接口
if (http != null) {
return http;
}
//初始化一个权限事件发布器,在filter执行流程中用到。
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
//该变量是WebSecurityConfigurerAdapter中的类型是AuthenticationManagerBuilder的实例变量,在此类中
//通过代码9初始化,将权限事件发布对象添加到该对象中
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//初始化一个权限管理器,详情看代码10
AuthenticationManager authenticationManager = authenticationManager();
//authenticationManager注入到authenticationBuilder
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
//创建一个sharedObjects 对象,暂不开始讲解,从代码22处讲解。
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
//向HttpSecurity对象中添加关于csfr 等相关配置类,从代码22处下开始讲解
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;
}
通过Autowired注解修改方法,在初始化对象过程中,会执行该方法,将authentioncation
Buildder和localConfigureAuthenticationBldr 变量注入属性。
代码9
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
//初始化一个密码编码器,
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
//初始化实例变量authenticationBuilder 和localConfigureAuthenticationBldr 变量。
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
//重写AuthenticationManagerBuilder类中的eraseCredentials,调用该方法时,也会调用authenticationBuilder 的eraseCredentials方法
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
};
}
authenticationManagerInitialized变量和disableLocalConfigureAuthenticationBldr分别来判定该类中authenticationManager和LocalConfigureAuthenticationBldr是否初始化过,刚开始都是false.
代码10
protected AuthenticationManager authenticationManager() throws Exception {
//如果authenticationManager没有初始化,因为该方法就是构造AuthenticationManager对象,所以没有初始化
if (!authenticationManagerInitialized) {
// 在代码9中可以看见LocalConfigureAuthenticationBldr变量已经初始化过了,在这里将该变量置为true
this.disableLocalConfigureAuthenticationBldr = true;
if (disableLocalConfigureAuthenticationBldr) {
//通过authenticationConfiguration(权限配置)构造一个authenticationManager对象。 getAuthenticationManager方法请看代码11
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
//在此时else不会执行,除非是多并发情况下,将disableLocalConfigureAuthenticationBldr变量改为flase,先放这里,后面再看
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
初始化AuthencationManager对象。
代码11
public AuthenticationManager getAuthenticationManager() throws Exception {
//如果authenticationManager已经初始化了,直接返回。
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
//此时的authcationManagerBuilder是AuthticationConfiguration类中的内部类
AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
//通过代码12 可以看出globalAuthConfigurers集合中有 EnableGlobalAuthenticationAutowiredConfigurer,InitializeAuthenticationProviderBeanManagerConfigurer,InitializeUserDetailsBeanManagerConfigurer对象
for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
authBuilder.apply(config);//将全局的权限配置类放到authenticationManagerBuidler中的Map中
}
//执行authentioncationManagerBuilder的doBuild方法,前面是执行的WebSecurity的doBuidld方法,请不要混淆!
前面已经介绍build方法实际上执行的是dobuild方法,doBuild方法执行的是配置类中的init和configure方法。请看代码13。
authenticationManager = authBuilder.build();
if (authenticationManager == null) {
authenticationManager = getAuthenticationManagerBean();
}
this.authenticationManagerInitialized = true;
return authenticationManager;
}
前面提到过Autowired注解用在方法上,可以在初始化对象的时候,执行该方法,并收集IOC容器中的bean对象,作为形参注入。并赋值给globalAuthConfigurers 集合,收集的对象在AuthticationConfiguration中初始化。如下面通过@Bean注解修饰的方法
代码12
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
List<GlobalAuthenticationConfigurerAdapter> configurers) {
configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigurers = configurers;
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
ApplicationContext context) {
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
此时this变量指向的是AuthencationManagerBuilder对象,与前面执行此方法不同的是,this指向的是webSecurity对象。然后执行配置类(EnableGlobalAuthenticationAutowiredConfigurer,InitializeUserDetailsBeanManagerConfigurer 和InitializeAuthenticationProviderBeanManagerConfigurer )的init和configure方法。最后执行performBuild方法。
代码13
protected final O doBuild() throws Exception {
synchronized (configurers) {
//从未初始化状态切换为初始化状态
buildState = BuildState.INITIALIZING;
//空实现,钩子方法
beforeInit();
//执行配置类的init方法,请看代码14,代码15,代码16
init();
buildState = BuildState.CONFIGURING;
//空实现,钩子方法
beforeConfigure();
//执行authenticationManager对象存储的配置类对象的configure方法。请参考代码18
configure();
buildState = BuildState.BUILDING;
//执行完configure方法后,执行performBuild方法,请参考代码21
O result = performBuild();
//将构建状态定义为 构建完毕状态
buildState = BuildState.BUILT;
return result;
}
}
EnableGlobalAuthenticationAutowiredConfigurer 类的init方法仅仅是起到打印的,并无实质作用。
代码14
private static class EnableGlobalAuthenticationAutowiredConfigurer extends
GlobalAuthenticationConfigurerAdapter {
private final ApplicationContext context;
private static final Log logger = LogFactory
.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);
EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) {
Map<String, Object> beansWithAnnotation = context
.getBeansWithAnnotation(EnableGlobalAuthentication.class);
if (logger.isDebugEnabled()) {
logger.debug("Eagerly initializing " + beansWithAnnotation);
}
}
}
InitializeUserDetailsBeanManagerConfigurer类中的init方法是初始化了一个InitializeUserDetailsManagerConfigurer配置类,由于BuildState已经处于初始化状态,所以会存放到authenticationManagerBuilder对象中的configurersAddedInInitializing List集合中,对此如果有疑惑,请参考代码2
代码15
class InitializeUserDetailsBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;
private final ApplicationContext context;
/**
* @param context
*/
InitializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
.....省略
}
InitializeAuthenticationProviderBeanManagerConfigurer类中的init方法是初始化了一个InitializeAuthenticationProviderManagerConfigurer配置类,由于BuildState已经处于初始化状态,所以会存放到authenticationManagerBuilder对象中的configurersAddedInInitializing List集合中,对此如果有疑惑,请参考代码2
代码16
class InitializeAuthenticationProviderBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER
- 100;
private final ApplicationContext context;
/**
* @param context the ApplicationContext to look up beans.
*/
InitializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
通过上述方法放到configurersAddedInInitializing集合中的对象会在 init方法中执行,由于上述对象所有的init是空实现,因此执行不会产生任何影响。
代码17
@SuppressWarnings("unchecked")
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);
}
}
此时this变量指向的是authenticationManager,由于apply通过for循环依次执行EnableGlobalAuthenticationAutowiredConfigurer,InitializeUserDetailsBeanManagerConfigurer 和InitializeAuthenticationProviderBeanManagerConfigurer ,InitializeUserDetailsMana
gerConfigurer,InitializeUserDetailsManagerConfigurer(虽然两者命名相同,但是在不同的类中,作为内部类存在)的configure方法。由EnableGlobalAuthenticationAutowiredConfigurer,
InitializeUserDetailsBeanManagerConfigurer 和InitializeAuthenticationProviderBean
ManagerConfigurer 类中并没重写父类GlobalAuthenticationConfigurerAdapter的configure方法,而父类中的configure方法是空实现,因此执行过程中是空执行,请参考代码19和代码20,对InitializeUserDetailsManagerConfigurer类的configure方法做详细说明
代码18
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
代码19片段是InitializeAuthenticationProviderBeanManagerConfigurer类中的内部类, 判断是狗初始化了AuthenticationProvider对象(也就是在代码20中提到的DaoAuthentication
Provider),显然最初是没有定义了,所以在代码20中定义了放到authenticationManagerBuilder对象中
代码19
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) {
if (auth.isConfigured()) {
return;
}
AuthenticationProvider authenticationProvider = getBeanOrNull(
AuthenticationProvider.class);
if (authenticationProvider == null) {
return;
}
auth.authenticationProvider(authenticationProvider);
}
当前对象this是anthenticationManagerBuilder。多次强调是代码有点绕,怕大家迷糊.
代码20片段是InitializeUserDetailsBeanManagerConfigurer 类中的内部类,是初始化了一个DaoAuthenticationProvider对象(注入UserDetailService属性,也就是如果我们要定义自己的用户登录的数据来源,必须要继承该接口,并定义其实现),添加到AuthenticationManagerBuilder类中
代码20
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//anthenticationManagerBuilder初始化时,authenticationProviders是空集,且parentAuthenticationManager 为null
if (!authenticationProviders.isEmpty() || parentAuthenticationManager != null) {
return;
}
//获取UserDetailsService对象,如果自己定义了,必须要使用@Service Bean等注解交给IOC容器管理,
//否则无法获取定义的UserDetailsService,则采用默认的 InMemoryUserDetails
UserDetailsService userDetailsService = getBeanOrNull(
UserDetailsService.class);
if (userDetailsService == null) {
return;
}
//获取一个密码编码器
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
//获取UserDetailsPasswordService ,IOC 容器中没有定义,默认为空
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
//初始化DaoAuthenticationProvider对象,在filter中会使用.
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder);
}
if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager);
}
//实际上是钩子方法,大家可以点进去看,是空实现
provider.afterPropertiesSet();
auth.authenticationProvider(provider);
}
}
最后执行authenticationManagerBuilder类中的performBuild。在init和configure方法中,主要初始化了DaoAuthencationProvider对象注入到authenticationManagerBuilder对象中.至此,AuthenticatonManager的初始化完成。方法执行返回到WebSecurityConfigurerAdapter的
getHttp方法中,从代码21开始,从此处开始讲解
代码21
@Override
protected ProviderManager performBuild() throws Exception {
//判断authenticationManagerBuilder中没有DaoAuthenticationProvider或者
//parentAuthenticationManager(初始化的时候并未初始化parentAuthenticationManager) 不为空就返回,
if (!authenticationProviders.isEmpty() || parentAuthenticationManager != null) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
//构建一个providerManager对象
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
//Boolean类型对象,未初始化,所以为null
if (eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
}
if (eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(eventPublisher);
}
//ProviderManager 实现了AuthenticationManager 接口
providerManager = postProcess(providerManager);
return providerManager;
}
通过上面的讲解,我们已经得知代码执行到了初始化AuthenticationManager对象,并生成了HttpSecurity对象。
代码22
protected final HttpSecurity getHttp() throws Exception {
//如果已经初始化 了HttpSecurity直接返回,HttpSecurity是关于Http相关的配置,比如对什么资源做拦截等,它实现了SecurityBuilder接口
if (http != null) {
return http;
}
//初始化一个权限事件发布器,在filter执行流程中用到。
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
//该变量是WebSecurityConfigurerAdapter中的类型是AuthenticationManagerBuilder的实例变量,在此类中
//通过代码9初始化,将权限事件发布对象添加到该对象中
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//初始化一个权限管理器,详情看代码10
AuthenticationManager authenticationManager = authenticationManager();
//authenticationManager注入到authenticationBuilder
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
//创建一个sharedObjects 对象
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
//向HttpSecurity对象中添加关于csrf等相关配置类
//初始化为false.
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
//获取IOC容器中继承了AbstractHttpConfigurer类的实现类,由于IOC容器中
//没有对象注入,获取到空集,我们可以定义自己的配置类,讲给IOC容器,再此就可以获取
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
//将自己定义的配置类,加入到HttpSecurity中
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
吹按
代码23
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
//localConfigureAuthenticationBldr对象初始化是空集
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects())
//往集合中注入userDetailsService对象,见代码24
sharedObjects.put(UserDetailsService.class, userDetailsService());
//注入应用上下文
sharedObjects.put(ApplicationContext.class, context);
//在webSecurityConfigurerAdpater中 直接通过new 初始化,不做讲解
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
//在webSecurityConfigurerAdpater中 直接通过new 初始化,不做讲解
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
return sharedObjects;
}
UserDetailsServiceDelegator实现了UserDetailsService接口,
代码24
protected UserDetailsService userDetailsService() {
//从IOC容器获取AuthenticationManagerBuilder对象,在代码20中已经得知,该
//对象已经注入了DaoAuthticationProvider对象
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(
localConfigureAuthenticationBldr, globalAuthBuilder));
}
csrf():防止跨站请求伪造。and():返回WebSecuirty对象,及this当前对象。addFilter(new WebAsyncManagerIntegrationFilter()):往处理器链第一次添加filter。exceptionHandling():
处理异常相关的配置类。headers():请求头相关配置。sessionManagement():session有关的配置,securityContext():securityContext相关配置等。每个方法几乎是一样的。 我们可以自定义重写WebSecurityConfigurerAdaper类中的Configure方法,自定义配置.csrf:防止跨站请求伪造。
getOrApply底层调用的是apply方法,前面已经介绍过,实际上该方法是将CsrfConfigurer配置添加到HttpSecurity(实现了SecurityBuilder接口)中的configurer 集合中。
代码25
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<>(context));
}
authorizeRequests():向httpSecurity类添加url拦截配置类(ExpressionInterceptUrlRegistry
)见代码27。anyRequest():向httpSecurity类添加请求url匹配配置类,见代码28(Request
MatcherConfigurer))
代码26
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();
}
getRegistry获取了ExpressionInterceptUrlRegistry对象。该类是ExpressionUrlAuthorization
Configurer的内部类并继承了ExpressionUrlAuthorizationConfigurer。因此该方法是向Http
Security对象中添加了URL拦截的配置类ExpressionInterceptUrlRegistry。方法结束后返回ExpressionInterceptUrlRegistry对象
注:由于security代码嵌套太多,为方便展示,某些地方将代码合并在同一个方法中,但代码逻辑不变
代码27
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context))
.getRegistry();
}
ANY_REQUEST是AnyRequestMatcher的实例。初始化了一个AuthorizedUrl并向该对象requestMatchers集合添加了anyRequestMathcer对象。AuthorizedUrl是ExpressionUrl
AuthorizationConfigurer的内部类。
代码28
public C anyRequest() {
Assert.state(!this.anyRequestConfigured, "Can't configure anyRequest after itself");
this.unmappedMatchers = Arrays.asList(requestMatchers);
//初始化了一个AuthorizedUrl并向该对象requestMatchers集合添加了anyRequestMathcer对象
C configurer = new AuthorizedUrl(requestMatchers)
this.anyRequestConfigured = true;
return configurer;
}
代码29
public ExpressionInterceptUrlRegistry authenticated() {
//authenticated 是内置的字符串,字面量是="authenticated";
attribute = authenticated ;
//初始化为false,可调用not()方法改为true
if (not) {
attribute = "!" + attribute;
}
//REGISTRY是ExpressionUrlAuthorizationConfigurer内置的ExpressionInterceptUrlRegistry
//对象,也就是通过代码27中 Registry()方法获取的对象。
//向REGISTRY对象中添加了UrlMapping对象
for (RequestMatcher requestMatcher : requestMatchers) {
REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(
requestMatcher, configAttributes));
}
return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
}
本文详细介绍了SpringSecurity的启动流程,从SpringSecurity简介到其架构图,重点解析了WebSecurityConfiguration类中的配置过程,包括配置类的加载、init和configure方法的执行,以及AuthenticationManager和HttpSecurity的构建。通过理解这一流程,有助于深入掌握SpringSecurity的工作原理。
5万+

被折叠的 条评论
为什么被折叠?



