五SpringSecurity重要配置类--2SecurityConfigurer

五SpringSecurity重要配置类--2SecurityConfigurer

Spring Security 过滤器链中的每一个过滤器,都是通过 xxxConfigurer 来进行配置的,而这些 xxxConfigurer 实际上都是 SecurityConfigurer 的实现。

SecurityConfigurer 本身是一个接口,我们来看下:

//参数对泛型使用:SecurityBuilder,表示方法需要传入的是builder类------用来构建过滤器链的
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
//初始化
	void init(B builder) throws Exception;
//配置
	void configure(B builder) throws Exception;
}

可以看到,SecurityConfigurer 中主要是两个方法,init 和 configure。

image-20230828224731189

SecurityConfigurer 有三个实现类:

  • SecurityConfigurerAdapter
  • GlobalAuthenticationConfigurerAdapter
  • WebSecurityConfigurer

我们分别来看

4.4.1 SecurityConfigurerAdapter

SecurityConfigurerAdapter 实现了 SecurityConfigurer 接口,我们所使用的大部分的 xxxConfigurer 也都是 SecurityConfigurerAdapter 的子类。

SecurityConfigurerAdapter 在 SecurityConfigurer 的基础上,还扩展出来了几个非常好用的方法

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
		implements SecurityConfigurer<O, B> {
	private B securityBuilder;
//objectPostProcessor内部类,用来向spring容器注册bean对象
	private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();

	public void init(B builder) throws Exception {
	}

	public void configure(B builder) throws Exception {
	}

  //and方法返回securityBuilder,用来继续添加配置
  /** and 方法,该方法返回值是一个 securityBuilder,securityBuilder 实际上就是 HttpSecurity,我们在 HttpSecurity 中去配置不同的过滤器时,可以使用 and 方法进行链式配置,就是因为这里定义了 and 方法并返回了 securityBuilder 实例。 */
	public B and() {
		return getBuilder();
	}
	protected final B getBuilder() {
		if (securityBuilder == null) {
			throw new IllegalStateException("securityBuilder cannot be null");
		}
		return securityBuilder;
	}
  
  
	@SuppressWarnings("unchecked")
	protected <T> T postProcess(T object) {
		return (T) this.objectPostProcessor.postProcess(object);
	}
	public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
		this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
	}

  
  
  //内部类
	private static final class CompositeObjectPostProcessor implements
			ObjectPostProcessor<Object> {
    //管理多个objectPostProcessor对象
		private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();

    
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Object postProcess(Object object) {
			for (ObjectPostProcessor opp : postProcessors) {
				Class<?> oppClass = opp.getClass();
        //当前objectPostProcessor所能解决的对象object类型
				Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,
						ObjectPostProcessor.class);
        //遍历查找可以处理的objectPostProcessor
				if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
					object = opp.postProcess(object);//调用objectPostProcessor处理,向容器中注册对象
				}
			}
			return object;
		}
    
    

}
  1. CompositeObjectPostProcessor 首先一开始声明了一个 CompositeObjectPostProcessor 实例,CompositeObjectPostProcessor 是 ObjectPostProcessor 的一个实现,ObjectPostProcessor 本身是一个后置处理器,该后置处理器默认有两个实现,AutowireBeanFactoryObjectPostProcessor 和 CompositeObjectPostProcessor。其中 AutowireBeanFactoryObjectPostProcessor 主要是利用了 AutowireCapableBeanFactory 对 Bean 进行手动注册,因为在 Spring Security 中,很多对象都是手动 new 出来的,这些 new 出来的对象和容器没有任何关系,利用 AutowireCapableBeanFactory 可以将这些手动 new 出来的对象注入到容器中,而 AutowireBeanFactoryObjectPostProcessor 的主要作用就是完成这件事;

    CompositeObjectPostProcessor 则是一个复合的对象处理器,里边维护了一个 List 集合,这个 List 集合中,大部分情况下只存储一条数据,那就是 AutowireBeanFactoryObjectPostProcessor,用来完成对象注入到容器的操作,如果用户自己手动调用了 addObjectPostProcessor 方法,那么 CompositeObjectPostProcessor 集合中维护的数据就会多出来一条,在 CompositeObjectPostProcessor#postProcess 方法中,会遍历集合中的所有 ObjectPostProcessor,挨个调用其 postProcess 方法对对象进行后置处理。

  2. and 方法,该方法返回值是一个 securityBuilder,securityBuilder 实际上就是 HttpSecurity,我们在 HttpSecurity 中去配置不同的过滤器时,可以使用 and 方法进行链式配置,就是因为这里定义了 and 方法并返回了 securityBuilder 实例。

这便是 SecurityConfigurerAdapter 的主要功能,后面大部分的 xxxConfigurer 都是基于此类来实现的。

SecurityConfigurerAdapter有如下两个主要的实现类:

image-20230828224803530

4.4.2.1 UserDetailsAwareConfigurer--配置用户类

这个配置类看名字大概就知道这是用来配置用户类的。

image-20230822175543844

AbstractDaoAuthenticationConfigurer

//AbstractDaoAuthenticationConfigurer 中所做的事情比较简单,主要是构造了一个默认的 DaoAuthenticationProvider,并为其配置 PasswordEncoder 和 UserDetailsService。

UserDetailsServiceConfigurer

//UserDetailsServiceConfigurer 重写了 AbstractDaoAuthenticationConfigurer 中的 configure 方法,在 configure 方法执行之前加入了 initUserDetailsService 方法,以方便开发展按照自己的方式去初始化 UserDetailsService。不过这里的 initUserDetailsService 方法是空方法。

UserDetailsManagerConfigurer

//UserDetailsManagerConfigurer 中实现了 UserDetailsServiceConfigurer 中定义的 initUserDetailsService 方法,具体的实现逻辑就是将 UserDetailsBuilder 所构建出来的 UserDetails 以及提前准备好的 UserDetails 中的用户存储到 UserDetailsService 中。

该类同时添加了 withUser 方法用来添加用户,同时还增加了一个 UserDetailsBuilder 用来构建用户,这些逻辑都比较简单,小伙伴们可以自行查看。

JdbcUserDetailsManagerConfigurer

JdbcUserDetailsManagerConfigurer 在父类的基础上补充了 DataSource 对象,同时还提供了相应的数据库查询方法。

InMemoryUserDetailsManagerConfigurer

//InMemoryUserDetailsManagerConfigurer 在父类的基础上重写了构造方法,将父类中的 UserDetailsService 实例定义为 InMemoryUserDetailsManager。

DaoAuthenticationConfigurer

//DaoAuthenticationConfigurer 继承自 AbstractDaoAuthenticationConfigurer,只是在构造方法中修改了一下 userDetailsService 而已。

示例:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("javaboy")
                .password("{noop}123")
                .roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                //省略
    }
}

当你调用 auth.inMemoryAuthentication 进行配置时,实际上调用的就是 InMemoryUserDetailsManagerConfigurer。

4.4.2.2 AbstractHttpConfigurer--security过滤器的父类

AbstractHttpConfigurer 这一派中的东西非常多,我们所有的过滤器配置,都是它的子类,我们来看下都有哪些。

image-20230822175611836

所有xxxConfigurer对应的filter已经在前面分析过,这里以FormLoginConfigurer为例,分析其逻辑:

1 AbstractHttpConfigurer

AbstractHttpConfigurer 继承自 SecurityConfigurerAdapter,并增加了两个方法,disable 和 withObjectPostProcessor:

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
		extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {


	@SuppressWarnings("unchecked")
	public B disable() {
    //从 getBuilder 中移除相关的 xxxConfigurer,getBuilder 方法获取到的实际上就是 HttpSecurity,所以移除掉 xxxConfigurer 实际上就是从过滤器链中移除掉某一个过滤器,例如 `.csrf().disable()` 就是移除掉处理 csrf 的过滤器
		getBuilder().removeConfigurer(getClass());
		return getBuilder();
	}

  //返回当前配置类xxxConfigurer,可以进行链式配置
	@SuppressWarnings("unchecked")
	public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
		addObjectPostProcessor(objectPostProcessor);
		return (T) this;
	}
}

disable ,我们常用的 .csrf().disable() 就是出自这里,那么从这里我们也可以看到 disable 的实现原理,就是从 getBuilder 中移除相关的 xxxConfigurer,getBuilder 方法获取到的实际上就是 HttpSecurity,所以移除掉 xxxConfigurer 实际上就是从过滤器链中移除掉某一个过滤器,例如 .csrf().disable() 就是移除掉处理 csrf 的过滤器。

另一个增加的方法是 withObjectPostProcessor,这是为配置类添加手动添加后置处理器的。在 AbstractHttpConfigurer 的父类中其实有一个类似的方法就是 addObjectPostProcessor,但是 addObjectPostProcessor 只是一个添加方法,返回值为 void,而 withObjectPostProcessor 的返回值是当前配置类,也就是 xxxConfigurer,所以如果使用 withObjectPostProcessor 的话,可以使用链式配置。

2 AbstractAuthenticationFilterConfigurer

image-20230822181020359

AbstractAuthenticationFilterConfigurer 类的功能比较多,源码也是相当相当长。不过我们只需要抓住两点即可,init 方法和 configure 方法,因为这两个方法是所有 xxxConfigurer 的灵魂。

@Override
public void init(B http) throws Exception {
	updateAuthenticationDefaults();
	updateAccessDefaults(http);
	registerDefaultAuthenticationEntryPoint(http);
}

init 方法主要干了三件事:

  1. updateAuthenticationDefaults 主要是配置了登录处理地址,失败跳转地址,注销成功跳转地址。
  2. updateAccessDefaults 方法主要是对 loginPage、loginProcessingUrl、failureUrl 进行 permitAll 设置(如果用户配置了 permitAll 的话)。
  3. registerDefaultAuthenticationEntryPoint 则是注册异常的处理器。

再来看 configure 方法:

@Override
public void configure(B http) throws Exception {
	PortMapper portMapper = http.getSharedObject(PortMapper.class);
	if (portMapper != null) {
		authenticationEntryPoint.setPortMapper(portMapper);
	}
	RequestCache requestCache = http.getSharedObject(RequestCache.class);
	if (requestCache != null) {
		this.defaultSuccessHandler.setRequestCache(requestCache);
	}
  
  //构建各种各样的回调函数设置给 authFilter
	authFilter.setAuthenticationManager(http
			.getSharedObject(AuthenticationManager.class));
	authFilter.setAuthenticationSuccessHandler(successHandler);
	authFilter.setAuthenticationFailureHandler(failureHandler);
	if (authenticationDetailsSource != null) {
		authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
	}
	SessionAuthenticationStrategy sessionAuthenticationStrategy = http
			.getSharedObject(SessionAuthenticationStrategy.class);
	if (sessionAuthenticationStrategy != null) {
		authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
	}
	RememberMeServices rememberMeServices = http
			.getSharedObject(RememberMeServices.class);
	if (rememberMeServices != null) {
		authFilter.setRememberMeServices(rememberMeServices);
	}
  //向容器注册filter对象
	F filter = postProcess(authFilter);
	http.addFilter(filter);
}

configure 中的逻辑就很简答了,构建各种各样的回调函数设置给 authFilter,authFilter 再去 postProcess 中走一圈注册到 Spring 容器中,最后再把 authFilter 添加到过滤器链中。

这便是 AbstractAuthenticationFilterConfigurer 的主要功能。需要提醒大家的是,我们日常配置的,如:

  • loginPage
  • loginProcessingUrl
  • permitAll
  • defaultSuccessUrl
  • failureUrl

等方法都是在这里定义的。

最后我们再来看看 FormLoginConfigurer。

3 FormLoginConfigurer

FormLoginConfigurer 对应的过滤器是 UsernamePasswordAuthenticationFilter。

image-20230822181800730

FormLoginConfigurer 在定义是,明确了 AbstractAuthenticationFilterConfigurer 中的泛型是 UsernamePasswordAuthenticationFilter,也就是我们这里最终要配置的过滤是 UsernamePasswordAuthenticationFilter。

FormLoginConfigurer 重写了 init 方法,配置了一下默认的登录页面。其他的基本上都是从父类来的,未做太多改变

4.4.2 GlobalAuthenticationConfigurerAdapter

GlobalAuthenticationConfigurerAdapter 看名字就知道是一个跟全局配置有关的东西,它本身实现了 SecurityConfigurerAdapter 接口,但是并未对方法做具体的实现,只是将泛型具体化了:

@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements
		SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {

	public void init(AuthenticationManagerBuilder auth) throws Exception {
	}

	public void configure(AuthenticationManagerBuilder auth) throws Exception {
	}
}

可以看到,SecurityConfigurer 中的泛型,现在明确成了 AuthenticationManager 和 AuthenticationManagerBuilder。所以 GlobalAuthenticationConfigurerAdapter 的实现类将来主要是和配置 AuthenticationManager 有关。当然也包括默认的用户名密码也是由它的实现类来进行配置的。

我们在 Spring Security 中使用的 AuthenticationManager 其实可以分为两种,一种是局部的,另一种是全局的,这里主要是全局的配置。

4.4.3 WebSecurityConfigurer---拓展用户自定义配置

image-20230823024147025

还有一个实现类就是 WebSecurityConfigurer,其实他就是我们天天用的 WebSecurityConfigurerAdapter 的父接口。

所以 WebSecurityConfigurer 的作用就很明确了,用户扩展用户自定义的配置。

1 WebSecurityConfigurer

WebSecurityConfigurer 其实是一个空接口,但是它里边约束了一些泛型,如下:

public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {

}

这里边的泛型很关键,这关乎到 WebSecurityConfigurer 的目的是啥!

  1. SecurityBuilder 中的泛型 Filter,表示 SecurityBuilder 最终的目的是为了构建一个 Filter 对象出来------在这里是,webSecurity.build->performBuild()方法创建FilterChainProxy。
  2. SecurityConfigurer 中两个泛型,第一个表示的含义也是 SecurityBuilder 最终构建的对象。

同时这里还定义了新的泛型 T,T 需要继承自 SecurityBuilder,根据 WebSecurityConfigurerAdapter 中的定义,我们可以知道,T 就是 WebSecurity,我们也大概能猜出 WebSecurity 就是 SecurityBuilder 的子类。

所以 WebSecurityConfigurer 的目的我们可以理解为就是为了配置 WebSecurity。

2 WebSecurityConfigurerAdapter

由于 WebSecurityConfigurer 只是一个空接口,WebSecurityConfigurerAdapter 就是针对这个空接口提供一个具体的实现,最终目的还是为了方便你配置 WebSecurity。

WebSecurityConfigurerAdapter 中的方法比较多,但是根据我们前面的分析,提纲挈领的方法就两个,一个是 init,还有一个 configure(WebSecurity web),其他方法都是为这两个方法服务的。那我们就来看下这两个方法:

先看 init 方法:

public void init(final WebSecurity web) throws Exception {
	final HttpSecurity http = getHttp();
  
  //将httpSecurity添加入WebSecurity 中,保存在 WebSecurity 的securityFilterChainBuilders 集合里
	web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
		FilterSecurityInterceptor securityInterceptor = http
				.getSharedObject(FilterSecurityInterceptor.class);
		web.securityInterceptor(securityInterceptor);
	});
}

//HttpSecurity 的初始化,实际上就是配置了一堆默认的过滤器,配置完成后,最终还调用了 configure(http) 方法,该方法又配置了一些拦截器
protected final HttpSecurity getHttp() throws Exception {
	if (http != null) {
		return http;
	}
	AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
	localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
	AuthenticationManager authenticationManager = authenticationManager();
	authenticationBuilder.parentAuthenticationManager(authenticationManager);
	Map<Class<?>, Object> sharedObjects = createSharedObjects();
  
  //创建httpSecurity
	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();
    
    //从spring容器中获取所有AbstractHttpConfigurer---即xxxConfigurer中
		List<AbstractHttpConfigurer> defaultHttpConfigurers =
				SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
		for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
      //将xxxConfigurer添加入httpSecurity中
			http.apply(configurer);
		}
	}
	configure(http);
	return http;
}
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();
}

init 方法可以算是这里的入口方法了:首先调用 getHttp 方法进行 HttpSecurity 的初始化。HttpSecurity 的初始化,实际上就是配置了一堆默认的过滤器,配置完成后,最终还调用了 configure(http) 方法,该方法又配置了一些拦截器,不过在实际开发中,我们经常会重写 configure(http) 方法,在之前示例中,configure(http) 方法几乎都有重写。HttpSecurity 配置完成后,再将 HttpSecurity 放入 WebSecurity 中,保存在 WebSecurity 的securityFilterChainBuilders 集合里。

posted @ 2023-08-28 22:59  LeasonXue  阅读(867)  评论(0)    收藏  举报