流程
1.接收请求
1、HTTP基本身份验证请求通过过滤器链直到它到达BasicAuthenticationFilter。
2、HTTP摘要式身份验证请求通过过滤器链,直到它到达DigestAuthenticationFilter。
3、登录表单提交请求(登录表单身份验证请求)通过过滤器链直到它到达UsernamePasswordAuthenticationFilter。
4、x509身份验证请求通过过滤器链直到它到达X509AuthenticationFilter等...
2.根据用户凭据创建AuthenticationToken
相关的AuthenticationFilter收到身份验证请求,会从收到的请求中提取用户名和密码(大多数身份验证机制都需要用户名和密码)。 之后,它会根据提取的用户凭据创建一个Authentication对象。 如果提取的凭据是用户名和密码,则将使用提取/找到的用户名和密码创建UsernamePasswordAuthenticationToken。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.security.core;
import java.io.Serializable;
import java.security.Principal;
import java.util.Collection;
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
3.创建的AuthenticationToken委派给AuthenticationManagager
它将用于调用AuthenticationManager的authenticate方法。 AuthenticationManager只是一个接口,实际的实现是ProviderManager。
public interface AuthenticationManager {
// 核心
Authentication authenticate(Authentication authentication)throws AuthenticationException;
}
ProviderManager有一个配置的AuthenticationProvider列表,应该用于验证用户请求。 ProviderManager将遍历每个提供的AuthenticationProvider,并尝试根据传递的Authentication Object对用户进行身份验证(例如: - UsernamePasswordAuthenticationToken)
4.尝试使用AuthenticationProvider列表进行身份验证
// 自定义验证器时需要实现
public interface AuthenticationProvider {
// 验证规则
Authentication authenticate(Authentication var1) throws AuthenticationException;
// 支持的Authentication类型
boolean supports(Class<?> var1);
}
以下是框架附带的一些现有身份验证程序:
- CasAuthenticationProvider
- JaasAuthenticationProvider
- DaoAuthenticationProvider
- OpenIDAuthenticationProvider
- RememberMeAuthenticationProvider
- LdapAuthenticationProvider
5. 验证数据来源:UserDetailsService
public interface UserDetailsService {
// 根据用户名获取用户信息,返回封装成UserDetail或实现返回
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
6、7. UserDetails 接口和 User 实现?
UserDetailsService将根据用户名检索UserDetails(实际实现是User)。
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
User实现:org.springframework.security.core.userdetails.User
8.返回Authentication 或者抛出 AuthenticationException?
如果用户成功通过身份验证,则将返回完全填充的Authentication对象。 否则将抛出AuthenticationException。
如果抛出任何AuthenticationException,那将由支持身份验证机制的已配置AuthenticationEntryPoint处理。
9.验证完成!
AuthenticationManager将获取的完全填充的Authentication对象返回到相关的Authentication 过滤器。
然后,相关的AuthenticationFilter会将获取的身份验证对象存储在SecurityContext中,以供将来过滤器使用。 (用于授权过滤器)
SecurityContextHolder.getContext().setAuthentication(authentication);
组件
SecurityContextHolder
SecurityContextHolder使用ThreadLocal来存储这些详细信息,这意味着安全上下文始终可用于同一执行线程中的方法,即使安全上下文未作为这些方法的参数显式传递
- 配置:
- SecurityContextHolder.MODE_GLOBAL策略:独立应用程序,全局共享用户信息
- SecurityContextHolder.MODE_INHERITABLETHREADLOCAL:安全线程生成的线程也采用相同的安全标识
// 从SecurityContextHolder中获取用户身份信息
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
UserDetailsService
数据来源:UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
校验实现:AuthenticationProvider
GrantedAuthority
Authentication提供的另一个重要方法是getAuthorities()。 此方法提供GrantedAuthority对象的数组。GrantedAuthority是授予主体的权限。 这些权限通常是“角色”,例如ROLE_ADMINISTRATOR或ROLE_HR_SUPERVISOR