SpringSecurity的启动流程

本文详细介绍了SpringSecurity的启动流程,从SpringSecurity简介到其架构图,重点解析了WebSecurityConfiguration类中的配置过程,包括配置类的加载、init和configure方法的执行,以及AuthenticationManager和HttpSecurity的构建。通过理解这一流程,有助于深入掌握SpringSecurity的工作原理。

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;
		}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值