二SpringSecurity核心组件类--1Security如何向容器添加bean--ObjectPostProcessor

二 SpringSecurity核心组件类--1 Security如何向容器添加bean--ObjectPostProcessor

从原理上,new出来对象和spring容器内bean无关,但是spring security框架中同样new很多对象,但是最终也被容器管理。这个功能是通过ObjectPostProcessor实现的。

给出如下动态权限配置代码:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                ...
    }
}
(1) ObjectPostProcessor作用

先来看下 ObjectPostProcessor 到底有啥作用,先来看一下这个接口的定义:

package org.springframework.security.config.annotation;
public interface ObjectPostProcessor<T> {
	<O extends T> O postProcess(O object);
}

接口中就只有一个 postProcess 方法。

我们再来看看 ObjectPostProcessor 的继承关系:

image-20230822143138692

两个比较重要的实现类,其中 AutowireBeanFactoryObjectPostProcessor 值得一说,来看下 AutowireBeanFactoryObjectPostProcessor 的定义:

final class AutowireBeanFactoryObjectPostProcessor
		implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {
	AutowireBeanFactoryObjectPostProcessor(
			AutowireCapableBeanFactory autowireBeanFactory) {
		this.autowireBeanFactory = autowireBeanFactory;
	}
	@SuppressWarnings("unchecked")
	public <T> T postProcess(T object) {
		if (object == null) {
			return null;
		}
		T result = null;
		try {
      //实例化、向spring容器注册对象
			result = (T) this.autowireBeanFactory.initializeBean(object,
					object.toString());
		}
		catch (RuntimeException e) {
			Class<?> type = object.getClass();
			throw new RuntimeException(
					"Could not postProcess " + object + " of type " + type, e);
		}
    //属性自动注入
		this.autowireBeanFactory.autowireBean(object);
		if (result instanceof DisposableBean) {
			this.disposableBeans.add((DisposableBean) result);
		}
		if (result instanceof SmartInitializingSingleton) {
			this.smartSingletons.add((SmartInitializingSingleton) result);
		}
		return result;
	}
}

AutowireBeanFactoryObjectPostProcessor 的源码很好理解:

  1. 首先在构建 AutowireBeanFactoryObjectPostProcessor 对象时,传入了一个 AutowireCapableBeanFactory 对象,看过 Spring 源码的小伙伴就知道,AutowireCapableBeanFactory 可以帮助我们手动的将一个实例注册到 Spring 容器中。
  2. 在 postProcess 方法中,就是具体的注册逻辑。

由此可见,ObjectPostProcessor 的主要作用就是手动注册实例到 Spring 容器中去(并且让实例走一遍 Bean 的生命周期)。

正常来说,我们项目中的 Bean 都是通过自动扫描注入到 Spring 容器中去的,然而在 Spring Security 框架中,有大量的代码不是通过自动扫描注入到 Spring 容器中去的,而是直接 new 出来的,这样做的本意是为了简化项目配置。

这些直接 new 出来的代码,如果想被 Spring 容器管理该怎么办呢?那就得 ObjectPostProcessor 出场了。

(2) 注册bean的案例--HttpSecurity/WebSecurity

exp1 HttpSecurity 初始化代码(WebSecurityConfigurerAdapter#getHttp):

protected final HttpSecurity getHttp() throws Exception {
	if (http != null) {
		return http;
	}
    ...
    ...
      //实例化HttpSecurity,并向其中传入objectPostProcessor对象
	http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
			sharedObjects);
	...
    ...
}

exp2 WebSecurity 初始化代码(WebSecurityConfiguration#setFilterChainProxySecurityConfigurer):

public void setFilterChainProxySecurityConfigurer(
		ObjectPostProcessor<Object> objectPostProcessor,
		@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
		throws Exception {
  //调用objectPostProcessor,向spring容器注册对象
	webSecurity = objectPostProcessor
			.postProcess(new WebSecurity(objectPostProcessor));
    ...
    ...
}
(3)filter注册入容器--xxxConfigure.configure->postProcess(xxxFilter)

Spring Security 框架源码中,随处可见手动装配。Spring Security 中,过滤器链中的所有过滤器都是通过对应的 xxxConfigure 来进行配置的,而所有的 xxxConfigure 都是继承自 SecurityConfigurerAdapter。

而在这些 xxxConfigure 的 configure 方法中,无一例外的都会让他们各自配置的管理器去 Spring 容器中走一圈,例如 AbstractAuthenticationFilterConfigurer#configure 方法:

public void configure(B http) throws Exception {
	...
    ...
    //xxxConfigure.configure->postProcess(xxxFilter),将filter注册入容器
	F filter = postProcess(authFilter);
	http.addFilter(filter);
}

其他的 xxxConfigurer#configure 方法也都有类似的实现

(4) 设计目的--withObjectPostProcessor添加自定义,实现对filter属性修改

直接将 Bean 通过自动扫描注册到 Spring 容器不好吗?为什么要搞成这个样子?

在 Spring Security 中,由于框架本身大量采用了 Java 配置,并且没有将对象的各个属性都暴露出来,这样做的本意是为了简化配置。然而这样带来的一个问题就是需要我们手动将 Bean 注册到 Spring 容器中去,ObjectPostProcessor 就是为了解决该问题。

一旦将 Bean 注册到 Spring 容器中了,我们就有办法去增强一个 Bean 的功能,或者需修改一个 Bean 的属性。

例如一开始提到的动态权限配置代码:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                ...
    }
}

权限管理本身是由 FilterSecurityInterceptor 控制的,系统默认的 FilterSecurityInterceptor 已经创建好了,而且我也没办法修改它的属性,那么怎么办呢?我们可以利用 withObjectPostProcessor 方法,去修改 FilterSecurityInterceptor 中的相关属性。

上面这个配置生效的原因之一是因为 FilterSecurityInterceptor 在创建成功后,会重走一遍 postProcess 方法,这里通过重写 postProcess 方法就能实现属性修改,我们可以看下配置 FilterSecurityInterceptor 的方法(AbstractInterceptUrlConfigurer#configure):

abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
		extends AbstractHttpConfigurer<C, H> {
	@Override
	public void configure(H http) throws Exception {
		FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
		if (metadataSource == null) {
			return;
		}
		FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
				http, metadataSource, http.getSharedObject(AuthenticationManager.class));
		if (filterSecurityInterceptorOncePerRequest != null) {
			securityInterceptor
					.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
		}
		securityInterceptor = postProcess(securityInterceptor);
		http.addFilter(securityInterceptor);
		http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
	}
}

可以看到,securityInterceptor 对象创建成功后,还是会去 postProcess 方法中走一遭。

看懂了上面的代码,接下来我再举一个例子小伙伴们应该一下就能明白:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                ...
                .and()
                .formLogin()
                .withObjectPostProcessor(new ObjectPostProcessor<UsernamePasswordAuthenticationFilter>() {
                    @Override
                    public <O extends UsernamePasswordAuthenticationFilter> O postProcess(O object) {
                        object.setUsernameParameter("name");
                        return object;
                    }
                })
                ...
    }
}

在这里,我把配置好的 UsernamePasswordAuthenticationFilter 过滤器再拎出来,修改一下用户名的 key(正常来说,修改用户名的 key 不用这么麻烦,这里主要是给大家演示 ObjectPostProcessor 的效果),修改完成后,以后用户登录时,用户名就不是 username 而是 name 了。

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