SpringSecurity------WebSecurityConfigurerAdapter配置适配器类
SpringSecurity------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、引入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;
}
本文地址:https://blog.csdn.net/sanjun333/article/details/111397756
上一篇: Led显示屏设备自发现技术实现-上海灵信
下一篇: 彻底剖析JVM类加载机制(一)
推荐阅读
-
CI(Codeigniter)的Setting增强配置类实例
-
VS2019添加Link to SQL类之后,LinqDataSource配置数据源没有可选项
-
python连接mongodb操作数据示例(mongodb数据库配置类)
-
【Config】类库读取自己的配置文件,配置文件的扩展
-
Spring5源码解析6-ConfigurationClassParser 解析配置类
-
Asp.net Core 和类库读取配置文件信息
-
Android在无需配置Application的情况下全局获取Context的类的方式
-
[Spring cloud 一步步实现广告系统] 5. 投放系统配置+启动+实体类
-
SpringBoot同一接口多个实现类配置
-
使用Java类加载SpringBoot、SpringCloud配置文件