SpringSecurity------WebSecurityConfigurerAdapter配置适配器类

一、属性

WebSecurityConfigurerAdapter属性

二、构造器

1、开启默认配置构建WebSecurityConfigurerAdapter

该种方式构建出来的配置不启用默认配置

protected WebSecurityConfigurerAdapter() {
	this(false);
}

2、指定是否开启默认配置构建WebSecurityConfigurerAdapter

protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
	this.disableDefaults = disableDefaults;
}

三、WebSecurityConfigurerAdapter主要做了什么

四、几个内部类

1、UserDetailsServiceDelegator

使用此代理类的主要作用是保证UserDetailsService被成功创建后再调用他的loadUserByUsername()方法

通过以下程序逻辑保证在调用loadUserByUsername()方法时,UserDetailsService已被成功创建:
(1)构造这个类时需要传入一组AuthenticationManagerBuilder,设置到delegateBuilders属性
(2)在loadUserByUsername()方法中遍历delegateBuilders属性值,获取UserDetails的一个实例(第一个构建成功的UserDetails会被立即设置到delegate字段,然后结束遍历)
(3)调用delegate自身的loadUserByUsername()方法

static final class UserDetailsServiceDelegator implements UserDetailsService {
    //AuthenticationManager建造器
	private List<AuthenticationManagerBuilder> delegateBuilders;
    //实际执行loadUserByUsername()方法获取UserDetails的类
	private UserDetailsService delegate;
    //一个锁对象,用于控制同步方法
	private final Object delegateMonitor = new Object();
    
	UserDetailsServiceDelegator(List<AuthenticationManagerBuilder> delegateBuilders) {
		Assert.isTrue(!delegateBuilders.contains(null),
				() -> "delegateBuilders cannot contain null values. Got " + delegateBuilders);
		this.delegateBuilders = delegateBuilders;
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		if (this.delegate != null) {
			return this.delegate.loadUserByUsername(username);
		}
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				for (AuthenticationManagerBuilder delegateBuilder : this.delegateBuilders) {
					this.delegate = delegateBuilder.getDefaultUserDetailsService();
					if (this.delegate != null) {
						break;
					}
				}
				if (this.delegate == null) {
					throw new IllegalStateException("UserDetailsService is required.");
				}
				this.delegateBuilders = null;
			}
		}
		return this.delegate.loadUserByUsername(username);
	}
}

2、AuthenticationManagerDelegator

类似UserDetailsServiceDelegator的作用,保证通过AuthenticationManager获取Authentication时,AuthenticationManager是已经被创建成功的

static final class AuthenticationManagerDelegator implements AuthenticationManager {

	private AuthenticationManagerBuilder delegateBuilder;

	private AuthenticationManager delegate;

	private final Object delegateMonitor = new Object();

	private Set<String> beanNames;

	AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder, ApplicationContext context) {
		Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");
		Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class,
				"parentAuthenticationManager");
		ReflectionUtils.makeAccessible(parentAuthMgrField);
		this.beanNames = getAuthenticationManagerBeanNames(context);
		validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder), this.beanNames);
		this.delegateBuilder = delegateBuilder;
	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		if (this.delegate != null) {
			return this.delegate.authenticate(authentication);
		}
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				this.delegate = this.delegateBuilder.getObject();
				this.delegateBuilder = null;
			}
		}
		return this.delegate.authenticate(authentication);
	}

	private static Set<String> getAuthenticationManagerBeanNames(ApplicationContext applicationContext) {
		String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
				AuthenticationManager.class);
		return new HashSet<>(Arrays.asList(beanNamesForType));
	}

	private static void validateBeanCycle(Object auth, Set<String> beanNames) {
		if (auth == null || beanNames.isEmpty() || !(auth instanceof Advised)) {
			return;
		}
		TargetSource targetSource = ((Advised) auth).getTargetSource();
		if (!(targetSource instanceof LazyInitTargetSource)) {
			return;
		}
		LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
		if (beanNames.contains(lits.getTargetBeanName())) {
			throw new FatalBeanException(
					"A dependency cycle was detected when trying to resolve the AuthenticationManager. "
							+ "Please ensure you have configured authentication.");
		}
	}
}

3、DefaultPasswordEncoderAuthenticationManagerBuilder

作用:拥有默认密码编码器的AuthenticationManager建造器

static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {

	private PasswordEncoder defaultPasswordEncoder;

		/**
		 * Creates a new instance
		 * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
		 */
	DefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
			PasswordEncoder defaultPasswordEncoder) {
		super(objectPostProcessor);
		this.defaultPasswordEncoder = defaultPasswordEncoder;
	}

	@Override
	public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
			throws Exception {
		return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
	}

	@Override
	public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
		return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
	}

	@Override
	public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
			T userDetailsService) throws Exception {
		return super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);
	}
}

4、LazyPasswordEncoder

作用:懒加载的密码编码器

该类的核心逻辑在getPasswordEncoder()方法,他首先会从容器中获取PasswordEncoder,如果没有获取到,则使用PasswordEncoderFactories创建一个PasswordEncoder(DelegatingPasswordEncoder)

static class LazyPasswordEncoder implements PasswordEncoder {

	private ApplicationContext applicationContext;

	private PasswordEncoder passwordEncoder;

	LazyPasswordEncoder(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	@Override
	public String encode(CharSequence rawPassword) {
		return getPasswordEncoder().encode(rawPassword);
	}

	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		return getPasswordEncoder().matches(rawPassword, encodedPassword);
	}

	@Override
	public boolean upgradeEncoding(String encodedPassword) {
		return getPasswordEncoder().upgradeEncoding(encodedPassword);
	}
	
	private PasswordEncoder getPasswordEncoder() {
		if (this.passwordEncoder != null) {
			return this.passwordEncoder;
		}
		PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
		if (passwordEncoder == null) {
			passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
		}
		this.passwordEncoder = passwordEncoder;
		return passwordEncoder;
	}
    //从ApplicationContext中获取PasswordEncoder的实现,没有获取到就返回空
	private <T> T getBeanOrNull(Class<T> type) {
		try {
			return this.applicationContext.getBean(type);
		}
		catch (NoSuchBeanDefinitionException ex) {
			return null;
		}
	}

	@Override
	public String toString() {
		return getPasswordEncoder().toString();
	}
}

五、依赖注入的方法(Spring构建过程中会触发这些方法执行)

1、引入ApplicationContext

这个方法是当前配置适配器的核心方法,从依赖注入的ApplicationContext对象中获取所需的实例对象,然后创建了两个AuthenticationManagerBuilder赋值给当前配置类的两个属性字段。

@Autowired
public void setApplicationContext(ApplicationContext context) {
    //注入ApplicationContext 
	this.context = context;
	//获取容器中唯一的一个ObjectPostProcessor实例:AutowireBeanFactoryObjectPostProcessor
	ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
	//创建一个懒加载的LazyPasswordEncoder实例,这个类是本类的一个内部类
	LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
	//创建一个默认的AuthenticationManagerBuilder,这个类是当前配置类的一个内部类
	this.authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor,
			passwordEncoder);
	//创建一个本地配置的AuthenticationManagerBuilder,重写了eraseCredentials()和authenticationEventPublisher()方法
	//使用当前配置类中的authenticationBuilder擦除认证凭证和发布事件
	this.localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(
			objectPostProcessor, passwordEncoder) {

		@Override
		public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
			WebSecurityConfigurerAdapter.this.authenticationBuilder.eraseCredentials(eraseCredentials);
			return super.eraseCredentials(eraseCredentials);
		}

		@Override
		public AuthenticationManagerBuilder authenticationEventPublisher(
				AuthenticationEventPublisher eventPublisher) {
			WebSecurityConfigurerAdapter.this.authenticationBuilder.authenticationEventPublisher(eventPublisher);
			return super.authenticationEventPublisher(eventPublisher);
		}
	};
}

2、引入AuthenticationConfigurationObjectPostProcessor

@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
	this.objectPostProcessor = objectPostProcessor;
}
@Autowired
public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
	this.authenticationConfiguration = authenticationConfiguration;
}

3、引入ContentNegotiationStrategy和AuthenticationTrustResolver

@Autowired(required = false)
public void setContentNegotationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) {
	this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@Autowired(required = false)
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
	this.trustResolver = trustResolver;
}

六、初始化方法

在这个方法中,首先获取HttpSecurity,这个对象包含了用户的自定义配置信息,然后将这个HttpSecurity对象存入WebSecurity中,然后创建一个Runnable线程,这个线程用来初始化WebSecurity的FilterSecurityInterceptor

@Override
public void init(WebSecurity web) throws Exception {
	HttpSecurity http = getHttp();
	web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
		FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
		web.securityInterceptor(securityInterceptor);
	});
}

这个方法是用来获取HttpSecurity实例的,同时给localConfigureAuthenticationBldr添加一个事件发布器,给authenticationBuilder添加一个父级AuthenticationManager

@SuppressWarnings({ "rawtypes", "unchecked" })
protected final HttpSecurity getHttp() throws Exception {
	if (this.http != null) {
		return this.http;
	}
	AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
	this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
	AuthenticationManager authenticationManager = authenticationManager();
	this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
	Map<Class<?>, Object> sharedObjects = createSharedObjects();
	this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
	if (!this.disableDefaults) {
		applyDefaultConfiguration(this.http);
		ClassLoader classLoader = this.context.getClassLoader();
		List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
				.loadFactories(AbstractHttpConfigurer.class, classLoader);
		for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
			this.http.apply(configurer);
		}
	}
	//这是一个可供子类实现的钩子方法,子类重写这个方法提供自定义配置
	configure(this.http);
	return this.http;
}

获取一个事件发布器

private AuthenticationEventPublisher getAuthenticationEventPublisher() {
	if (this.context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {
		return this.context.getBean(AuthenticationEventPublisher.class);
	}
	return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
}

获取一个AuthenticationManager

protected AuthenticationManager authenticationManager() throws Exception {
	if (!this.authenticationManagerInitialized) {
		configure(this.localConfigureAuthenticationBldr);
		if (this.disableLocalConfigureAuthenticationBldr) {
			this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
		}
		else {
			this.authenticationManager = this.localConfigureAuthenticationBldr.build();
		}
		this.authenticationManagerInitialized = true;
	}
	return this.authenticationManager;
}

创建一些共享对象

private Map<Class<?>, Object> createSharedObjects() {
	Map<Class<?>, Object> sharedObjects = new HashMap<>();
	sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());
	sharedObjects.put(UserDetailsService.class, userDetailsService());
	sharedObjects.put(ApplicationContext.class, this.context);
	sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
	sharedObjects.put(AuthenticationTrustResolver.class, this.trustResolver);
	return sharedObjects;
}

默认的配置

private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
	http.csrf();
	http.addFilter(new WebAsyncManagerIntegrationFilter());
	http.exceptionHandling();
	http.headers();
	http.sessionManagement();
	http.securityContext();
	http.requestCache();
	http.anonymous();
	http.servletApi();
	http.apply(new DefaultLoginPageConfigurer<>());
	http.logout();
}

七、配置入口方法

这是一个模板方法,可以由子类实现,然后提供自定义的配置

protected void configure(HttpSecurity http) throws Exception {
	this.logger.debug("Using default configure(HttpSecurity). "
			+ "If subclassed this will potentially override subclass configure(HttpSecurity).");
	http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
	http.formLogin();
	http.httpBasic();
}

这是一个钩子方法,使用该配置类的实例会调用该方法,所以这里我们可以自定义修改一些WebSecurity配置

@Override
public void configure(WebSecurity web) throws Exception {
}

这是一个模板方法,可以由子类实现,然后提供自定义的配置

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	this.disableLocalConfigureAuthenticationBldr = true;
}

八、配置入口方法

protected UserDetailsService userDetailsService() {
	AuthenticationManagerBuilder globalAuthBuilder = this.context.getBean(AuthenticationManagerBuilder.class);
	return new UserDetailsServiceDelegator(Arrays.asList(this.localConfigureAuthenticationBldr, globalAuthBuilder));
}
public UserDetailsService userDetailsServiceBean() throws Exception {
	AuthenticationManagerBuilder globalAuthBuilder = this.context.getBean(AuthenticationManagerBuilder.class);
	return new UserDetailsServiceDelegator(Arrays.asList(this.localConfigureAuthenticationBldr, globalAuthBuilder));
}
public AuthenticationManager authenticationManagerBean() throws Exception {
	return new AuthenticationManagerDelegator(this.authenticationBuilder, this.context);
}
protected final ApplicationContext getApplicationContext() {
	return this.context;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豢龙先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值