您当前的位置: 首页 >  spring

陈橙橙丶

暂无认证

  • 0浏览

    0关注

    107博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

SpringSecurity(十二)过滤器链分析(上)

陈橙橙丶 发布时间:2022-03-16 14:44:19 ,浏览量:0

前言

说到Spring Security的实现原理,大部分都知道是通过过滤器链。因为Spring Security中的所有功能都是通过过滤器链来实现的,这些过滤器组成一个完整的过滤器链。那么这些过滤器链是如何初始化的?我们之前说的AuthenticationManager又是如何初始化的?前面我们对Spring Security的一些核心过滤器以及组件有了一定的了解。由于初始化流程相对复杂,因此我们并没有一开始从这一块分析。

初始化流程分析

Spring Security初始化流程整体上来说还是很好理解的,但是这里涉及的零碎知识比较多,因此这里先介绍一下常见的关键组件,在理解这些组件的基础上,在来分析初始化流程。

ObjectPostProcessor

ObjectPostProcessor是Spring Security中使用频率最高的组件之一,它是一个对象后置处理器,也就是当一个对象创建成功后,如果还有一些额外的事情需要补充,那么可以通过ObjectPostProcessor来进行处理。这个接口中只有一个默认的postProcess

ObjectPostProcessor默认有两个继承类: 在这里插入图片描述

  • AutowireBeanFactoryObjectPostProcessor:由于SpringSecurity中大量采用了JAVA配置,许多过滤器都是直接new出来的,这些直接new出来的对象并不会自动注入到Spring容器中。Spring Security这样做的本意是简化配置,但是却带来了另一个问题,大量new出来的对象需要我们手动注册到Spring容器中。AutowireBeanFactoryObjectPostProcessor对象所承担的就是这件事情,一个对象new出来之后,只要调用AutowireBeanFactoryObjectPostProcessor#postProcess方法就可以成功注入到Spring容器中,它的实现原理就是通过调用Spring容器中的AutowireCapableBeanFactory对象将一个new出来的对象注入到Spring容器中。

  • CompositeObjectPostProcessor:这是另一个实现,一个对象可以有一个后置处理器,我们也可以自定义多个对象后置处理器。CompositeObjectPostProcessor是一个组合的对象后置处理器。他里面维护了一个List集合,集合中存放了某一个对象的所有后置处理器,当需要执行对的后置 处理器时,会遍历集合中的所有ObjectPostProcess实例,分别调用实例的postProcess方法进行对象后置处理。在Spring Security框架中,最终使用的对象后置处理器其实就是CompositeObjectPostProcessor,他里面的集合默认只有一个对象,就是AutowireBeanFactoryObjectPostProcessor。

在Spring Security中,我们可以灵活的配置 项目中需要哪些Spring Security过滤器,一旦选的过滤器之后,每一个过滤器都会有一个对应的配置器,叫做xxxConfigurer(例如:CorsConfigurer、CsrfConfigurer),过滤器都是在xxxConfigurer中new出来的,然后在postProcess方法中处理一遍,就将这些过滤器注入到Spring容器中了。

这就是对象后置处理器ObjectPostProcessor的主要作用。

SecurityFilterChain

从名字上看,我们可以知道SecurityFilterChain是Spring Security中的过滤器链对象:

public interface SecurityFilterChain {

	boolean matches(HttpServletRequest request);

	List getFilters();

}
  • matches:该方法用来判断request请求是否应该被当前过滤器链所处理。
  • getFilters:该方法返回一个List集合,集合中存放的就是Spring Security中的过滤器。如果matches方法返回true,那么request请求就会在该方法所返回的Filter集合中被处理。

SecurityFilterChain只有一个默认的实现类DefaultSecurityFilterChain,器中定义了两个属性,并具体实现了SecurityFilterChain中的两个方法:

public final class DefaultSecurityFilterChain implements SecurityFilterChain {

	private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);

	private final RequestMatcher requestMatcher;

	private final List filters;

	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
		this(requestMatcher, Arrays.asList(filters));
	}

	public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List filters) {
		logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
		this.requestMatcher = requestMatcher;
		this.filters = new ArrayList(filters);
	}

	public RequestMatcher getRequestMatcher() {
		return this.requestMatcher;
	}

	@Override
	public List getFilters() {
		return this.filters;
	}

	@Override
	public boolean matches(HttpServletRequest request) {
		return this.requestMatcher.matches(request);
	}

	@Override
	public String toString() {
		return this.getClass().getSimpleName() + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters
				+ "]";
	}

}

可以看到,在DefaultSecurityFilterChain的构造方法中,需要传入两个参数,一个是请求匹配器requestMatcher,另一个则是过滤器集合或者过滤器数组Filter。这个类的实现很简单。

*需要注意的是,在一个Spring Security项目中,SecurityFilterChain的实例可能会有多个。

SecurityBuilder

Spring Secuirty中所有需要构建的对象都可以通过SecurityBuilder来实现,默认的过滤器链、代理过滤器、AuthenticationManager等,都可以通过SecurityBuilder来构建。

在这里插入图片描述

SecurityBuilder,我们先开看看它的源码:

public interface SecurityBuilder {

	
	O build() throws Exception;

}

可以看到接口中只有一个build方法,就是对象构建方法。build方法的返回值,就是具体构建的对象泛型O,也就是说不通的SecurityBuild将会构建出不同的对象。

HttpSecurityBuilder

HttpSecurityBuilder是用来构建HttpSecurity对象的,HttpSecurityBuilder的定义如下

public interface HttpSecurityBuilder
		extends SecurityBuilder {

	
	 C getConfigurer(Class clazz);

	
	 C removeConfigurer(Class clazz);

	
	 void setSharedObject(Class sharedType, C object);

	
	 C getSharedObject(Class sharedType);

	
	H authenticationProvider(AuthenticationProvider authenticationProvider);

	
	H userDetailsService(UserDetailsService userDetailsService) throws Exception;

	H addFilterAfter(Filter filter, Class afterFilter);

	
	H addFilterBefore(Filter filter, Class beforeFilter);

	
	H addFilter(Filter filter);

}

我们简单分析一下:

(1)、HttpSecurityBuilder对象本身在定义时就有一个泛型,这个泛型是HttpSecurityBuilder的子类,由于默认情况下HttpSecurityBuilder的实现类只有一个HTTPSecurity,所以可暂且把接口中的H 都当成HttpSecurity

(2)、HttpSecurityBuilder继承自SecurityBuild接口,同时也指定了SecurityBuilder中的泛型为DefaultSecurityFilterChain,也就是说,HttpSecurityBuilder最终想要构建的对象就是DefaultSecurityFilterChain

(3)、getConfigurer方法用来获取一个配置器,也就是xxxConfigurer,我们在后续会说到

(4)、removeConfigurer方法用来移除一个配置器(相当于从Spring Security过滤器链中移除一个过滤器)。

(5)、setSharedObject/getSharedObject这两个方法用来设置或获取一个可以在多个配置器之间共享的对象。

(6)、authenticationProvider方法可以用来配置一个认证器AuthenticationProvider

(7)、userDetailsService方法可以用来配置一个数据源UserDetailsService

(8)、addFilterAfter/addFilterBefore方法表示在某一个过滤器之后或者之前添加一个自定义的过滤器。

(9)、addFilter方法可以添加一个过滤器,这个过滤器必须是SpringSecurity提供的过滤器的一个实例或者其扩展,会自动进行过滤器的排序。

AbstractSecurityBuilder

AbstractConfiguredSecurityBuilder实现了ScurityBuilder接口,并对build做了完善,确保只build一次,我们来看一下:

public abstract class AbstractSecurityBuilder implements SecurityBuilder {

	private AtomicBoolean building = new AtomicBoolean();

	private O object;

	@Override
	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;

}

由上述可知,在AbstractSecurityBuilder类中:

(1)、首先声明了building变量,可以确保即使在多线程环境下,配置类也只构建一次。

(2)、对build方法进行重写,并且设置final,这样在AbstractSecurityBuilder的子类中将不能再次重写build方法。在build方法内部,通过building变量来控制配置类只构建一次,具体的构建工作则交给doBuild方法去完成。

(3)、getObject方法用来返回构建的对象。

(4)、doBuild方法则是具体的构建方法,该方法在AbstractSecurityBuilder中是一个抽象方法,具体的实现在其子类中。

AbstractConfiguredSecurityBuilder

AbstractConfiguredSecurityBuilder的源码较长,我们分开来看。

首先在AbstractConfiguredSecurityBuilder中声明一个枚举,用来描述构建过程的不同状态

private enum BuildState {

		
		UNBUILT(0),

		
		INITIALIZING(1),

		CONFIGURING(2),

		BUILDING(3),

		
		BUILT(4);

		private final int order;

		BuildState(int order) {
			this.order = order;
		}

		public boolean isInitializing() {
			return INITIALIZING.order == this.order;
		}

		
		public boolean isConfigured() {
			return this.order >= CONFIGURING.order;
		}

	}
  • UNBUILT:配置类构建前
  • INITIALIZING:初始化中(初始化完成之前是这个状态)
  • CONFIGURING:配置中(开始构建之前是这个状态)
  • BUILDING:构建中
  • BUILT:构建完成

这个枚举里面还提供了两个判断方法,isInitializing表示是否正在初始化中,isConfigured方法表示是否已完成配置。

AbstractConfiguredSecurityBuilder中还生命了configurers变量,用来保存所有的配置类。针对configurers变量,我们可以进行添加配置、移除配置等操作,相关方法如下:

public abstract class AbstractConfiguredSecurityBuilder
		extends AbstractSecurityBuilder {
    
    		private final LinkedHashMap configurers = new LinkedHashMap();

		public  C apply(C configurer) throws Exception {
		configurer.addObjectPostProcessor(this.objectPostProcessor);
		configurer.setBuilder((B) this);
		add(configurer);
		return configurer;
	}
    
    public  C apply(C configurer) throws Exception {
		add(configurer);
		return configurer;
	}
    
    private  void add(C configurer) {
		Assert.notNull(configurer, "configurer cannot be null");
		Class clazz = (Class) configurer
				.getClass();
		synchronized (this.configurers) {
			if (this.buildState.isConfigured()) {
				throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
			}
			List configs = null;
			if (this.allowConfigurersOfSameType) {
				configs = this.configurers.get(clazz);
			}
			configs = (configs != null) ? configs : new ArrayList(1);
			configs.add(configurer);
			this.configurers.put(clazz, configs);
			if (this.buildState.isInitializing()) {
				this.configurersAddedInInitializing.add(configurer);
			}
		}
	}
    
    public  List getConfigurers(Class clazz) {
		List configs = (List) this.configurers.get(clazz);
		if (configs == null) {
			return new ArrayList();
		}
		return new ArrayList(configs);
	}
    
    public  List removeConfigurers(Class clazz) {
		List configs = (List) this.configurers.remove(clazz);
		if (configs == null) {
			return new ArrayList();
		}
		return new ArrayList(configs);
	}
    
    public  C getConfigurer(Class clazz) {
		List configs = this.configurers.get(clazz);
		if (configs == null) {
			return null;
		}
		Assert.state(configs.size() == 1,
				() -> "Only one configurer expected for type " + clazz + ", but got " + configs);
		return (C) configs.get(0);
	}
    
    public  C removeConfigurer(Class clazz) {
		List configs = this.configurers.remove(clazz);
		if (configs == null) {
			return null;
		}
		Assert.state(configs.size() == 1,
				() -> "Only one configurer expected for type " + clazz + ", but got " + configs);
		return (C) configs.get(0);
	}
    
    private Collection getConfigurers() {
		List result = new ArrayList();
		for (List configs : this.configurers.values()) {
			result.addAll(configs);
		}
		return result;
	}
}

我们来看一下:

(1)、首先声明了一个configurers变量,用来保存所有的配置类,key是配置类Class对象,值是List集合中放着配置类。

(2)、apply方法有两个,参数类型略有差异,主要功能基本一致,都是向configurer变量中添加配置类,具体的添加过程则是调用add方法。

(3)、add方法用来将所有的配置类保存到configurers中,在添加过程中,如果allowConfigurersOfSameType变量变为true,则表示允许相同类型的配置类存在,也就是List集合中可以存在多个相同类型的配置类。默认情况下,如果是普通配置类,allowConfigurersOfSameType是false,所以List集合中的配置类始终只有一个配置类;如果在AuthenticationManagerBuilder中设置allowConfigurersOfSameType为true,此时相同类型的配置可以有多个支持。

(4)、getConfigurers方法可以从configurers中返回某一个配置类对应的所有实例。

(5)、removeConfigurer方法是一个私有方法,主要把所有的配置类实例放到一个集合中返回。在配置类初始化和配置的时候,会调用该方法。

这就是AbstractConfiguredSecurityBuilder中关于configurers的所有操作。

接下来就是AbstractConfiguredSecurityBuilder中的doBuild方法,这是核心的构建方法:

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

protected void beforeInit() throws Exception {
	}

protected void beforeConfigure() throws Exception {
	}
private void configure() throws Exception {
		Collection configurers = getConfigurers();
		for (SecurityConfigurer configurer : configurers) {
			configurer.configure((B) this);
		}
	}
protected abstract O performBuild() throws Excep
    
    private void init() throws Exception {
		Collection configurers = getConfigurers();
		for (SecurityConfigurer configurer : configurers) {
			configurer.init((B) this);
		}
		for (SecurityConfigurer configurer : this.configurersAddedInInitializing) {
			configurer.init((B) this);
		}
	}

(1)、在doBuild方法中,一遍更新构建状态,一遍执行构建方法。在构建方法中,beforeInit是一个空的初始化方,如果需要在初始化之前做一些准备工作,可以通过重写该方法实现。

(2)、init方法是所有配置类的初始化方法,在该方法中,遍历所有的配置类,并调用其init方法完成初始化操作。

(3)、beforeConfigure方法可以在configure方法执行之前做一些准备操作。该方法默认也是一个空方法。

(4)、configure方法用来完成所有配置类的配置,在configure方法中,遍历所有的配置类,分别调用configure方法完成配置。

(5)、performBuild方法用来做最终的构建操作,前面的准备工作完成后,最后在performBuild方法中完成构建,这是一个抽象方法,具体的实现则在不同的配置类中。

这就是AbstractConfiguredSecurityBuilder中最主要的几个方法,其他一些方法比较简单了就。

ProviderManagerBuilder

ProviderManagerBuilder继承自SecurityBuild接口,并指定了构建的对象是AuthenticationManager,如下:

public interface ProviderManagerBuilder
		extends SecurityBuilder {

	
	B authenticationProvider(AuthenticationProvider authenticationProvider);

}

可以看到,ProviderManagerBuilder中增加了一个authenticationProvider方法,同时通过泛型指定了构建的对象为AuthenticationManager。

AuthenticationManagerBuilder

AuthenticationManagerBuilder用来构建AuthenticationManager对象,它继承自AbstractConfiguredSecurityBuilder,并且实现了ProviderManagerBuilder接口,我们截取部分常用代码来看:

public class AuthenticationManagerBuilder
		extends AbstractConfiguredSecurityBuilder
		implements ProviderManagerBuilder {

	
    	public AuthenticationManagerBuilder(ObjectPostProcessor objectPostProcessor) {
		super(objectPostProcessor, true);
	}
    
	
    	public AuthenticationManagerBuilder parentAuthenticationManager(AuthenticationManager authenticationManager) {
		if (authenticationManager instanceof ProviderManager) {
			eraseCredentials(((ProviderManager) authenticationManager).isEraseCredentialsAfterAuthentication());
		}
		this.parentAuthenticationManager = authenticationManager;
		return this;
	}
	
    
    	public InMemoryUserDetailsManagerConfigurer inMemoryAuthentication()
			throws Exception {
		return apply(new InMemoryUserDetailsManagerConfigurer());
	}
    
    	public JdbcUserDetailsManagerConfigurer jdbcAuthentication() throws Exception {
		return apply(new JdbcUserDetailsManagerConfigurer());
	}
    
    	public  DaoAuthenticationConfigurer userDetailsService(
			T userDetailsService) throws Exception {
		this.defaultUserDetailsService = userDetailsService;
		return apply(new DaoAuthenticationConfigurer(userDetailsService));
	}
	
    
    	@Override
	public AuthenticationManagerBuilder authenticationProvider(AuthenticationProvider authenticationProvider) {
		this.authenticationProviders.add(authenticationProvider);
		return this;
	}
    
    
	@Override
	protected ProviderManager performBuild() throws Exception {
		if (!isConfigured()) {
			this.logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
			return null;
		}
		ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
				this.parentAuthenticationManager);
		if (this.eraseCredentials != null) {
			providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
		}
		if (this.eventPublisher != null) {
			providerManager.setAuthenticationEventPublisher(this.eventPublisher);
		}
		providerManager = postProcess(providerManager);
		return providerManager;
	}
    
}

(1)、首先在AuthenticationManagerBuilder构造方法中,调用了父类的构造方法,注意第二参数传递了true,表示允许相同类型的配置类同时存在。

(2)、parentAuthenticationManager 方法用来给一个AuthenticationManager设置parent。

(3)、inMemoryAuthentication方法用来配置基于内存的数据源,该方法会自动创建InMemoryUserDetailsManagerConfigurer配置类,并最终将该配置类添加到父类的configurers变量中,由于设置了允许相同类型的配置类同时存在,因此该方法可以反复调用多次。

(4)、jdbcAuthentication以及userDetailsService方法上(3)方法类似。

(5)、authenticationProvider方法用来向authenticaitonProviders集合添加AuthenticationProvider对象,根据前几篇文章说的,我们已经知道一个AuthenticationManager实例中包含多个AuthenticationProvider实例,那么多个AuthenticationProvider实例可以通过authenticationProvider方法进行添加。

(6)、performBuild 方法则只需具体的构建工作,常用的AuthenticationManager实例就是ProviderManager,所以这里创建ProviderManager对象,并且配置authenticationProviders和parentAuthenticationManager对象,ProviderManager对象创建成功之后,再去创建后置处理器中处理一遍在返回。

这就是AuthenticationManagerBuilder中的一个大致逻辑。

关注
打赏
1648473527
查看更多评论
立即登录/注册

微信扫码登录

0.0681s