SpringSecurity------WebSecurityConfigurerAdapter配置适配器
-
- 一、属性
- 二、构造器
-
- 1、开启默认配置构建WebSecurityConfigurerAdapter
- 2、指定是否开启默认配置构建WebSecurityConfigurerAdapter
- 三、WebSecurityConfigurerAdapter主要做了什么
- 四、几个内部类
-
- 1、UserDetailsServiceDelegator
- 2、AuthenticationManagerDelegator
- 3、DefaultPasswordEncoderAuthenticationManagerBuilder
- 4、LazyPasswordEncoder
- 五、依赖注入的方法(Spring构建过程中会触发这些方法执行)
-
- 1、引入ApplicationContext
- 2、引入[AuthenticationConfiguration](https://blog.csdn.net/sanjun333/article/details/111682876)和[ObjectPostProcessor](https://blog.csdn.net/sanjun333/article/details/111998763)
- 3、引入ContentNegotiationStrategy和AuthenticationTrustResolver
- 六、初始化方法
- 七、配置入口方法
- 八、配置入口方法
一、属性
二、构造器
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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()throws Exception {
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);}@Overridepublic JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);}@Overridepublic <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;}@Overridepublic String encode(CharSequence rawPassword) {
return getPasswordEncoder().encode(rawPassword);}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {
return getPasswordEncoder().matches(rawPassword, encodedPassword);}@Overridepublic 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;}}@Overridepublic String toString() {
return getPasswordEncoder().toString();}
}
五、依赖注入的方法(Spring构建过程中会触发这些方法执行)
1、引入ApplicationContext
这个方法是当前配置适配器的核心方法,从依赖注入的ApplicationContext对象中获取所需的实例对象,然后创建了两个AuthenticationManagerBuilder赋值给当前配置类的两个属性字段。
@Autowired
public void setApplicationContext(ApplicationContext context) {
//注入ApplicationContext this.context = context;//获取容器中唯一的一个ObjectPostProcessor实例:AutowireBeanFactoryObjectPostProcessorObjectPostProcessor<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) {
@Overridepublic AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
WebSecurityConfigurerAdapter.this.authenticationBuilder.eraseCredentials(eraseCredentials);return super.eraseCredentials(eraseCredentials);}@Overridepublic AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
WebSecurityConfigurerAdapter.this.authenticationBuilder.authenticationEventPublisher(eventPublisher);return super.authenticationEventPublisher(eventPublisher);}};
}
2、引入AuthenticationConfiguration和ObjectPostProcessor
@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;
}