当前位置: 代码迷 >> 综合 >> SpringSecurity------WebSecurityConfigurerAdapter配置适配器类
  详细解决方案

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

热度:42   发布时间:2023-12-20 23:12:44.0

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
    • 六、初始化方法
    • 七、配置入口方法
    • 八、配置入口方法

一、属性

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;}@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;
}
  相关解决方案