SpringBoot3-Security 之 认证与UsernamePasswordAuthenticationFilter

文章目录

  • 前言
  • 一、用户相关的类
    • 认证信息 Authentication
      • 常见的 Authentication 的子类
        • 匿名用户身份信息 AnonymousAuthenticationToken
        • 用户名密码认证信息 UsernamePasswordAuthenticationToken
    • 用户信息 UserDetails
      • 常见的 UserDetails 子类
        • User
    • 用户信息操作类 UserDetailsService
      • 常见的子类
        • 用户管理器 UserDetailsManager
        • 基于内存 InMemoryUserDetailsManager
        • 基于数据库 JdbcUserDetailsManager
  • 二、认证相关类
    • 认证服务提供者 AuthenticationProvider
      • 主要实现类
        • 匿名用户认证服务提供者 AnonymousAuthenticationProvider
        • 持久化用户认证服务提供者 DaoAuthenticationProvider
    • 认证管理器 AuthenticationManager
        • 认证服务管理器 ProviderManager - 极其重要
        • ProviderManager 的认证流程
  • 三、认证流程
    • 1. UsernamePasswordAuthenticationFilter#doFilter
      • 1.1 UsernamePasswordAuthenticationFilter#requiresAuthentication
    • 2. UsernamePasswordAuthenticationFilter#attemptAuthentication
    • 3. ProviderManager#authenticate
  • 四、过滤器的构建
    • AuthenticationManagerBuilder
    • 认证自动配置类 `AuthenticationConfiguration`
      • InitializeUserDetailsBeanManagerConfigurer
      • InitializeAuthenticationProviderBeanManagerConfigurer
    • AuthenticationManager 的构建过程
      • 1. HttpSecurity 的创建
      • 2. HttpSecurity 的构建之前
      • 3. UsernamePasswordAuthenticationFilter 加入到 HttpSecurity 过滤器链
  • 总结


前言

本文主要研究 SpringBoot-Security 的认证过程, 主要涉及的过滤器是 UsernamePasswordAuthenticationFilter

  1. 首先我们要学习认证涉及到的类, 主要是多个接口之间的关系和作用
  2. 学习 UsernamePasswordAuthenticationFilter 中做了什么
  3. 学习 UsernamePasswordAuthenticationFilter 中一些重要属性的创建过程

一、用户相关的类

认证信息 Authentication

该类存储用户的身份信息, 权限信息, 以及一些附加信息

public interface Authentication extends Principal, Serializable {
	// 权限信息
	Collection<? extends GrantedAuthority> getAuthorities();
	// 凭据,一般获取到的是密码
	Object getCredentials();
	// 从请求中获取到的一些附加信息
	Object getDetails();
	// 一般是用户名或者 UserDetails
	Object getPrincipal();
	// 是否已经认证
	boolean isAuthenticated();
	// 设置认证结果
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

常见的 Authentication 的子类

匿名用户身份信息 AnonymousAuthenticationToken

没有登录的用户直接访问接口的话, 会给该请求一个 AnonymousAuthenticationToken 的权限

用户名密码认证信息 UsernamePasswordAuthenticationToken

通过用户名密码进行登录的时候, SpringSecurity 对于该用户的信息存储的实现

用户信息 UserDetails

SpringSecurity 内置的用户信息的接口

常见的 UserDetails 子类

User

注意是 org.springframework.security.core.userdetails 包下的

public class User implements UserDetails, CredentialsContainer {
	// 密码
	private String password;
	// 用户名
	private final String username;
	// 权限
	private final Set<GrantedAuthority> authorities;
	// 账户是否过期
	private final boolean accountNonExpired;
	// 是否被锁定
	private final boolean accountNonLocked;
	// 密码是否过期
	private final boolean credentialsNonExpired;
	// 是否激活状态
	private final boolean enabled;
}

用户信息操作类 UserDetailsService

UserDetailsService: 用来通过用户名获取用户信息的接口
UserDetailsManager: 用来对用户进行增删改查的操作类
一般都会同时实现这两个接口

public interface UserDetailsService {
	// 通过用户名获取用户信息
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

常见的子类

用户管理器 UserDetailsManager

继承了 UserDetailsService, 在其基础上增加了对用户进行增删改查的功能

public interface UserDetailsManager extends UserDetailsService {
	void createUser(UserDetails user);
	void updateUser(UserDetails user);
	void deleteUser(String username);
	void changePassword(String oldPassword, String newPassword);
	boolean userExists(String username);
}
基于内存 InMemoryUserDetailsManager

基于内存的用户管理类. 同时实现了 UserDetailsService 和 UserDetailsManager

基于数据库 JdbcUserDetailsManager

基于数据库的用户管理类, 同时实现了 UserDetailsService 和 UserDetailsManager
一般咱们自己的业务不会使用这个, 而是自己直接实现 UserDetailsService

二、认证相关类

认证服务提供者 AuthenticationProvider

真正提供认证业务处理的类

public interface AuthenticationProvider {
	// 认证, 认证成功返回一个新的认证成功的 Authentication
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
	// 判断 Provider 是否支持处理当前的 Authentication
	boolean supports(Class<?> authentication);
}

主要实现类

匿名用户认证服务提供者 AnonymousAuthenticationProvider

认证匿名用户信息 AnonymousAuthenticationToken

持久化用户认证服务提供者 DaoAuthenticationProvider

最常用的认证服务, 一般我们认为用来认证从数据库中取出来的信息, UsernamePasswordAuthenticationToken 用户的认证使用这个类来处理
注: 这个类里面核心业务就只是从使用 UserDetailService 中获取用户信息, 有很好的扩展性

认证管理器 AuthenticationManager

认证流程的入口

public interface AuthenticationManager {
	// 认证, 主要用来判断用户是否存在, 是否可用等操作
	Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
认证服务管理器 ProviderManager - 极其重要

AuthenticationProvider 的管理器, ProviderManager 中保存了一个 AuthenticationProvider 的集合, 认证流程启动之后, 循环调用 AuthenticationProvider#supports 并判断当前 AuthenticationProvider 是否能够认证当前的用户信息 UsernamePasswordAuthenticationToken, 如果能够处理则就用当前 AuthenticationProvider 处理认证流程
认证成功返回一个认证成功的 Authentication, 认证失败一般是抛出异常

ProviderManager 的认证流程

后面段落会描述


三、认证流程

1. UsernamePasswordAuthenticationFilter#doFilter

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	// 如果该请求不需要认证, 继续下面的 filter
	if (!requiresAuthentication(request, response)) {
		chain.doFilter(request, response);
		return;
	}
	try {
		// 核心方法一: attemptAuthentication() 获取认证的结果
		Authentication authenticationResult = attemptAuthentication(request, response);
		if (authenticationResult == null) {
			// return immediately as subclass has indicated that it hasn't completed
			return;
		}
		this.sessionStrategy.onAuthentication(authenticationResult, request, response);
		// Authentication success
		if (this.continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}
		// 认证成功之后的操作, 保存用户信息到 SecurityContextRepository, 执行 successHandler 等
		successfulAuthentication(request, response, chain, authenticationResult);
	}
	// 认证失败会跑出异常, 认证失败的回调
	catch (InternalAuthenticationServiceException failed) {
		this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
		unsuccessfulAuthentication(request, response, failed);
	}
	catch (AuthenticationException ex) {
		// Authentication failed
		unsuccessfulAuthentication(request, response, ex);
	}
}

1.1 UsernamePasswordAuthenticationFilter#requiresAuthentication

默认情况下 SpringSecurity 只有 /login 接口需要进行认证, 别的接口都直接放行了
SpringBoot3-Security 之 认证与UsernamePasswordAuthenticationFilter_第1张图片

2. UsernamePasswordAuthenticationFilter#attemptAuthentication

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
		throws AuthenticationException {
	// 仅支持 post 请求
	if (this.postOnly && !request.getMethod().equals("POST")) {
		throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
	}
	// 获取用户名和密码
	String username = obtainUsername(request);
	username = (username != null) ? username.trim() : "";
	String password = obtainPassword(request);
	password = (password != null) ? password : "";
	// 创建一个没有认证的 Authentication
	UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
	// Allow subclasses to set the "details" property
	// 设置 Authentication 的 detail 属性
	setDetails(request, authRequest);
	// 使用 AuthenticationManager 去认证 Authentication
	return this.getAuthenticationManager().authenticate(authRequest);
}

3. ProviderManager#authenticate

为什么是 ProviderManager 后面的章节会详细说

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
	Class<? extends Authentication> toTest = authentication.getClass();
	AuthenticationException lastException = null;
	AuthenticationException parentException = null;
	Authentication result = null;
	Authentication parentResult = null;
	int currentPosition = 0;
	int size = this.providers.size();
	// 获取所有的 AuthenticationProvider
	for (AuthenticationProvider provider : getProviders()) {
		// 判断当前 provider 是否能够处理当前的 Authentication
		if (!provider.supports(toTest)) {
			continue;
		}
		if (logger.isTraceEnabled()) {
			logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
					provider.getClass().getSimpleName(), ++currentPosition, size));
		}
		try {
			// 使用 Provider 认证, 如果认证成功会得到一个认证成功的 Authentication
			result = provider.authenticate(authentication);
			if (result != null) {
				copyDetails(authentication, result);
				break;
			}
		}
		catch (AccountStatusException | InternalAuthenticationServiceException ex) {
			prepareException(ex, authentication);
			// SEC-546: Avoid polling additional providers if auth failure is due to
			// invalid account status
			throw ex;
		}
		catch (AuthenticationException ex) {
			lastException = ex;
		}
	}
	// 如果上面的 Provider 全部判断了之后没有能处理认证的 Provider
	// 会获取到当前 Provider#parent, parent 同样也是一个 AuthenticationManager, 然后用该 parent 进行认证
	// 注: 其实这个 ProviderManager 的 parent 应该还是一个 ProviderManager, 只不过里面的 provider 不一样
	// 第四节会详细说这个字段的设置过程
	if (result == null && this.parent != null) {
		// Allow the parent to try.
		try {
			parentResult = this.parent.authenticate(authentication);
			result = parentResult;
		}
		catch (ProviderNotFoundException ex) {
			// ignore as we will throw below if no other exception occurred prior to
			// calling parent and the parent
			// may throw ProviderNotFound even though a provider in the child already
			// handled the request
		}
		catch (AuthenticationException ex) {
			parentException = ex;
			lastException = ex;
		}
	}
	if (result != null) {
		if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
			// Authentication is complete. Remove credentials and other secret data
			// from authentication
			// 认证成功, 移除凭证和一些安全的数据信息
			((CredentialsContainer) result).eraseCredentials();
		}
		// If the parent AuthenticationManager was attempted and successful then it
		// will publish an AuthenticationSuccessEvent
		// This check prevents a duplicate AuthenticationSuccessEvent if the parent
		// AuthenticationManager already published it
		// 这个地方的意思是说: 如果是 parent 认证成功的, 那么 parent 已经发不过认证成功的事件了, 这里就不需要发布了
		if (parentResult == null) {
			this.eventPublisher.publishAuthenticationSuccess(result);
		}

		return result;
	}
	// 处理认证失败或者抛出的异常
	// Parent was null, or didn't authenticate (or throw an exception).
	if (lastException == null) {
		lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",
				new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));
	}
	// If the parent AuthenticationManager was attempted and failed then it will
	// publish an AbstractAuthenticationFailureEvent
	// This check prevents a duplicate AbstractAuthenticationFailureEvent if the
	// parent AuthenticationManager already published it
	if (parentException == null) {
		prepareException(lastException, authentication);
	}
	throw lastException;
}

四、过滤器的构建

AuthenticationManagerBuilder

// 由此可见 AuthenticationManagerBuilder 本身就是一个 SecurityBuilder, 最后会执行 performBuild()
// 从实现接口 ProviderManagerBuilder 不难看出, 这个类构建出来的对象就是一个 ProviderManager
public class AuthenticationManagerBuilder
		extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
		implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
@Override
protected ProviderManager performBuild() throws Exception {
	if (!isConfigured()) {
		this.logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
		return null;
	}
	// 新建 ProviderManager 并设置 parent
	ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
			this.parentAuthenticationManager);
	if (this.eraseCredentials != null) {
		providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
	}
	if (this.eventPublisher != null) {
		providerManager.setAuthenticationEventPublisher(this.eventPublisher);
	}
	providerManager = postProcess(providerManager);
	return providerManager;
}

认证自动配置类 AuthenticationConfiguration

  1. authenticationManagerBuilder()ioc 中注入了一个 AuthenticationManagerBuilder, 用来构建 AuthenticationManager

    @Bean
    public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
    		ApplicationContext context) {
    	LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
    	AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
    	DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
    			objectPostProcessor, defaultPasswordEncoder);
    	if (authenticationEventPublisher != null) {
    		result.authenticationEventPublisher(authenticationEventPublisher);
    	}
    	return result;
    }
    
  2. 注入了 3 个Bean 都是 GlobalAuthenticationConfigurerAdapter 的实现类, 这三个类都是 AuthenticationManagerBuilder 的配置类

    @Bean
    public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
    		ApplicationContext context) {
    	return new EnableGlobalAuthenticationAutowiredConfigurer(context);
    }
    
    @Bean
    public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
    		ApplicationContext context) {
    	// 这个比较重要
    	return new InitializeUserDetailsBeanManagerConfigurer(context);
    }
    
    @Bean
    public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
    		ApplicationContext context) {
    	return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
    }
    
  3. 使用 @Autowired 设置了一些属性

    // 重要: 获取到所有的 GlobalAuthenticationConfigurerAdapter(就是2步骤注入的3个Bean) 然后赋值给 this.globalAuthConfigurers
    @Autowired(required = false)
    public void setGlobalAuthenticationConfigurers(List<GlobalAuthenticationConfigurerAdapter> configurers) {
    	configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
    	this.globalAuthConfigurers = configurers;
    }
    
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
    	this.applicationContext = applicationContext;
    }
    
    @Autowired
    public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
    	this.objectPostProcessor = objectPostProcessor;
    }
    
  4. 有一个 getAuthenticationManager() 方法, 在 HttpSecurity 构建的时候会调用, 这个很重要

    public AuthenticationManager getAuthenticationManager() throws Exception {
    	if (this.authenticationManagerInitialized) {
    		return this.authenticationManager;
    	}
    	// 从 ioc 中获取 AuthenticationManagerBuilder
    	AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
    	if (this.buildingAuthenticationManager.getAndSet(true)) {
    		return new AuthenticationManagerDelegator(authBuilder);
    	}
    	// 主要: 将 globalAuthConfigurers 中的配置都应用到 AuthenticationManager 上
    	for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
    		authBuilder.apply(config);
    	}
    	// 然后构建对应的对象, 其实就是 ProviderManager
    	this.authenticationManager = authBuilder.build();
    	if (this.authenticationManager == null) {
    		this.authenticationManager = getAuthenticationManagerBean();
    	}
    	this.authenticationManagerInitialized = true;
    	return this.authenticationManager;
    }
    

InitializeUserDetailsBeanManagerConfigurer

初始化 UserDetailsService 的配置

@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
	// 在 init 的时候加入了另外的一个配置类
	auth.apply(new InitializeUserDetailsManagerConfigurer());
}

InitializeUserDetailsManagerConfigurer#configure

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
	// 这里获取 ioc 中的 UserDetailsServices
	// 如果我们没有自己注入的话, 会获取到 UserDetailsServiceAutoConfiguration 中注入的 InMemoryUserDetailsManager
	List<BeanWithName<UserDetailsService>> userDetailsServices = getBeansWithName(UserDetailsService.class);
	... // 省略一部分代码
	// 创建一个 Provider
	DaoAuthenticationProvider provider;
	if (passwordEncoder != null) {
		provider = new DaoAuthenticationProvider(passwordEncoder);
	}
	else {
		provider = new DaoAuthenticationProvider();
	}
	provider.setUserDetailsService(userDetailsService);
	...
	// 向 AuthenticationManagerBuilder 中加了一个 DaoAuthenticationProvider
	auth.authenticationProvider(provider);
	...
}

InitializeAuthenticationProviderBeanManagerConfigurer

// InitializeAuthenticationProviderBeanManagerConfigurer#init
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
	// 添加了一个新的配置类
	auth.apply(new InitializeAuthenticationProviderManagerConfigurer());
}

InitializeAuthenticationProviderManagerConfigurer#configure

@Override
public void configure(AuthenticationManagerBuilder auth) {
	// auth.isConfigured() 是判断 AuthenticationManagerBuilder 中是否存在 AuthenticationProvider
	if (auth.isConfigured()) {
		return;
	}
	List<BeanWithName<AuthenticationProvider>> authenticationProviders = getBeansWithName(
			AuthenticationProvider.class);
	...
	// 将 ioc 中的 AuthenticationProvider 添加到 AuthenticationManagerBuilder 中
	auth.authenticationProvider(authenticationProvider);
	...
}

AuthenticationManager 的构建过程

1. HttpSecurity 的创建

// HttpSecurityConfiguration
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
	// 创建一个密码加密器
	LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
	// 新创建一个 AuthenticationManagerBuilder, 是不是很奇怪这地方为什么要在创建一个新的 AuthenticationManagerBuilder
	AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
	// 重要: authenticationManager() 会调用 AuthenticationConfiguration.getAuthenticationManager()
	// 把 AuthenticationConfiguration 中注入的那个 AuthenticationManager 当做新创建的 DefaultPasswordEncoderAuthenticationManagerBuilder 中构建的 AuthenticationManager 的父AuthenticationManager
	authenticationBuilder.parentAuthenticationManager(authenticationManager());
	// HttpSecurity 的构造函数中会把新创建的 AuthenticationManagerBuilder 放入到 SharedObjects 中, 等待某一刻别的什么地方把这个 AuthenticationManagerBuilder 取出来, 然后构建成 AuthenticationManager, 接着向下看
	HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
	...
	return http;
}

2. HttpSecurity 的构建之前

@Override
protected void beforeConfigure() throws Exception {
	// 如果已经有了 authenticationManager, 将 authenticationManager 放到 SharedObject 中
	if (this.authenticationManager != null) {
		setSharedObject(AuthenticationManager.class, this.authenticationManager);
	}
	// 构建 AuthenticationManager
	else {
		ObservationRegistry registry = getObservationRegistry();
		// 从SharedObject 中获取到 AuthenticationManagerBuilder 然后构建出来 AuthenticationManager
		// 然后将构建出来的 AuthenticationManager 在放到 SharedObject 中
		AuthenticationManager manager = getAuthenticationRegistry().build();
		if (!registry.isNoop() && manager != null) {
			setSharedObject(AuthenticationManager.class, new ObservationAuthenticationManager(registry, manager));
		}
		else {
			setSharedObject(AuthenticationManager.class, manager);
		}
	}
}

3. UsernamePasswordAuthenticationFilter 加入到 HttpSecurity 过滤器链

  1. HttpSecurityformLogin 方法

    public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
    		// 向 HttpSecurity 中加了 FormLoginConfigurer,在 HttpSecurity 构建的时候执行 performBuild() 的时候会执行
    		FormLoginConfigurer 的 
    		formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
    		return HttpSecurity.this;
    	}
    
  2. FormLoginConfigurer 的构造器

    	public FormLoginConfigurer() {
    		// 新建了一个 UsernamePasswordAuthenticationFilter 认证过滤器, 父类 AbstractAuthenticationFilterConfigurer 会保存一下这个过滤器
    		super(new UsernamePasswordAuthenticationFilter(), null);
    		usernameParameter("username");
    		passwordParameter("password");
    	}
    
    
  3. FormLoginConfigurerconfigure() 方法执行

    基本上 UsernamePasswordAuthenticationFilter 的大部分属性都在这里设置的

    // authFilter 就是 2 步骤传给父类的 UsernamePasswordAuthenticationFilter
    @Override
    public void configure(B http) throws Exception {
    	// 从 HttpSecurity 中获取到 AuthenticationManager 设置到 UsernamePasswordAuthenticationFilter
    	this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
    	// 设置认证成功处理器和认证失败处理器
    	this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
    	this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
    	...
    	F filter = postProcess(this.authFilter);
    	// 将 UsernamePasswordAuthenticationFilter 添加到 httpSecurity 中
    	http.addFilter(filter);
    }
    

总结

  1. 总的认证过程一共创建了两个 AuthenticationManagerBuilder, 其中 HttpSecurityConfiguration 中在创建 HttpSecurity 对象的时候创建了一个, AuthenticationConfiguration 中使用 @Bean 的方式注入了一个
  2. HttpSecurity 中创建的 AuthenticationManagerBuilder@Bean 创建的 AuthenticationManagerBuilder 作为自己的 parent 保存
  3. AuthenticationConfiguration 中有个注入了一个 InitializeUserDetailsBeanManagerConfigurer, 这个 config 在调用 AuthenticationConfiguration#getAuthenticationManager() 方法的时候会被加入到 @Bean 注入的 AuthenticationManagerBuilder 中, InitializeUserDetailsBeanManagerConfigurer 这个类会引入另外一个配置类, 然后该配置类会向 AuthenticationManagerBuilder 中添加一个 DaoAuthenticationProvider
  4. HttpSecurity 中创建的 AuthenticationManagerBuilder 会被放到 shareObject 中, 然后在 httpSecurity#beforeConfigure() 执行的时候会构建出来一个 AuthenticationManager 放到 shareObject
  5. 如果配置了 httpSecurity.formLogin() 就会在 httpSecurity 中加入 UsernamePasswordAuthenticationFilter, 同时会向 httpSecurity 中加入一个 FormLoginConfigurer 的配置
  6. FormLoginConfigurer 在执行 configure 的时候会从 httpSecurityshareObject 中获取到存放在 shareObject 中的 AuthenticationManager, 然后给 UsernamePasswordAuthenticationFilter 对应的属性赋值
  7. 验证: ProviderManager@8987 有一个 parent 为 ProviderManager@9005, ProviderManager@8987 里面有一个 AnonymousAuthenticationProvider, 而 ProviderManager@9005 没有 parent, 有一个 provider 为 DaoAuthenticationProvider
    SpringBoot3-Security 之 认证与UsernamePasswordAuthenticationFilter_第2张图片
  8. 问题: 如果如果配置 httpSecurity.anonymous() 会存在第一个 ProviderManager

    不可能不存在的, 因为 httpSecurity 在创建的时候就已经默认已经调用了 anonymous 方法, 加入了 AnonymousAuthenticationProvider

你可能感兴趣的:(springSecurity,spring,boot)