认证流程
图片来自于:黑马程序员SpringSecurity认证课程
认证过程:
- 用户提交用户名、密码被SecurityFilterChain中的UsernamePasswordAuthenticationFilter 过滤器获取到,封装为请求Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
- 然后过滤器将Authentication提交至认证管理器(AuthenticationManager)进行认证
- 认证成功后, AuthenticationManager身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除) Authentication 实例。
- SecurityContextHolder 安全上下文容器将第3步填充了信息的 Authentication ,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。可以看出AuthenticationManager接口(认证管理器)是认证相关的核心接口,也是发起认证的出发点,它
的实现类为ProviderManager。而Spring Security支持多种认证方式,因此ProviderManager维护着一个
List 列表,存放多种认证方式,最终实际的认证工作是由
AuthenticationProvider完成的。咱们知道web表单的对应的AuthenticationProvider实现类为
DaoAuthenticationProvider,它的内部又维护着一个UserDetailsService负责UserDetails的获取。最终
AuthenticationProvider将UserDetails填充至Authentication。
认证核心组件的大体关系如下:
图片来自于:黑马程序员SpringSecurity认证课程
知识点认识
Authentication
我们所面对的系统中的用户,在Spring Security中被称为主体(principal)。主体包含了所有能够经过验证而获得系统访问权限的用户、设备或其他系统。主体的概念实际上来自 Java Security,Spring Security通过一层包装将其定义为一个Authentication。
public interface Authentication extends Principal, Serializable {
// 获取主体授权列表
Collection<? extends GrantedAuthority> getAuthorities();
// 获取主体凭证,一般为密码
Object getCredentials();
// 获取主体携带的详细信息
Object getDetails();
// 获取主体,通常为username
Object getPrincipal();
// 获取当前主体是否认证成功
boolean isAuthenticated();
// 设置当前主体是否认证成功状态
void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
AuthenticateProvider
Spring Security 认证的过程其实就是一个构建Authentication的过程。Authentication 在Spring Security的各个AuthenticationProvider中流动,AuthenticationProvider被Spring Security定义为一个验证过程:
public interface AuthenticationProvider {
// 验证完成,成功,返回一个验证完成的Authentication
Authentication authenticate(Authentication var1) throws AuthenticationException;
// 是否支持验证当前的Authentication类型
boolean supports(Class<?> var1);
}
大部分场景下身份验证都是基于用户名和密码进行的,所以Spring Security提供了一个UsernamePasswordAuthenticationToken用于代指这一类证。,每一个登录用户即主体都被包装为一个UsernamePasswordAuthenticationToken,从而在Spring Security的各个AuthenticationProvider中流动。
ProviderManager
一次完整的认证可以包含多个AuthenticationProvider,一般由ProviderManager管理。
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
private static final Log logger = LogFactory.getLog(ProviderManager.class);
private AuthenticationEventPublisher eventPublisher;
// AuthenticationProvider 列表
private List<AuthenticationProvider> providers;
protected MessageSourceAccessor messages;
private AuthenticationManager parent;
private boolean eraseCredentialsAfterAuthentication;
public ProviderManager(List<AuthenticationProvider> providers) {
this(providers, (AuthenticationManager)null);
}
public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
this.eventPublisher = new ProviderManager.NullEventPublisher();
this.providers = Collections.emptyList();
this.messages = SpringSecurityMessageSource.getAccessor();
this.eraseCredentialsAfterAuthentication = true;
Assert.notNull(providers, "providers list cannot be null");
this.providers = providers;
this.parent = parent;
this.checkState();
}
public void afterPropertiesSet() {
this.checkState();
}
private void checkState() {
if (this.parent == null && this.providers.isEmpty()) {
throw new IllegalArgumentException("A parent AuthenticationManager or a list of AuthenticationProviders is required");
}
}
// 迭代AuthenticationProvider 列表,进行认证,返回最终结果
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;
boolean debug = logger.isDebugEnabled();
Iterator var8 = this.getProviders().iterator();
// 迭代
while(var8.hasNext()) {
AuthenticationProvider provider = (AuthenticationProvider)var8.next();
// 判断AuthenticationProvider 是否支持当前验证
if (provider.supports(toTest)) {
if (debug) {
logger.debug("Authentication attempt using " + provider.getClass().getName());
}
try {
// 执行AuthenticationProvider的认证。
result = provider.authenticate(authentication);
if (result != null) {
this.copyDetails(authentication, result);
// 有一个验证通过,就返回
break;
}
} catch (InternalAuthenticationServiceException | AccountStatusException var13) {
this.prepareException