spring-security–基础–02–核心组件
1、SecurityContextHolder
- 最基本的对象
- 它负责存储当前安全上下文信息。
- 保存着当前用户是什么,是否已经通过认证,拥有哪些权限.
- 默认使用ThreadLocal策略来存储认证信息,这是一种与线程绑定的策略。
- 在Web场景下的使用Spring Security,在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息。
1.1、获取有关当前用户的信息
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
2、Authentication
package org.springframework.security.core;
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean var1)throws IllegalArgumentException;
}
- 是Spring Security中最高级别的认证。
- getAuthorities:获取权限列表
- getCredentials:获取密码信息,由用户输入的密码凭证,认证之后会移出,来保证安全性;
- getDetails:细节信息,web应用中的实现接口通常为WebAuthenticationDetails,它记录了访问者的ip地址和sessionId的值。
- getPrincipal:获取身份信息,一般返回UserDetails的实现类;
3、UserDetails
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
- 代表了最详细的用户信息
- 这个接口涵盖了一些必要的用户信息字段
3.1、和Authentication区别
- Authentication中的getCredentials来源于用户提交的密码凭证,而UserDetails中的getPassword取到是用户正确的密码信息。
- Authentication中的getAuthorities是认证用户名和密码成功之后,由UserDetails中的getAuthorities传递而来。而Authentication中的getDetails信息是经过了AuthenticationProvider认证之后填充的。
4、UserDetailsService
public interface UserDetailsService {
UserDetails loadUserByUsername(String username)throws UsernameNotFoundException;
}
- loadUserByUsername:就是从特定的地方(一般是从数据库中)加载用户信息。
- 常用的实现类
- JdbcDaoImpl:从数据库中加载用户
- InMemoryUserDetailsManager:从内存中加载用户
- 可以自己实现UserDetailsService
5、AuthenticationManager
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)throws AuthenticationException;
}
- authenticate:
- 用于认证
- 认证相关的核心接口
- 发起认证的出发点
- Spring推荐通过实现AuthenticationManager接口来自定义自己的认证方式
- 默认的实现:ProviderManager
- 保留了关键认证部分的 ProviderManager 源码:
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
// 维护一个 AuthenticationProvider 列表
private List<AuthenticationProvider> providers = Collections.emptyList();
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
// 依次认证
for(AuthenticationProvider provider : getProviders()){
if(!provider.supports(toTest)){
continue;
}
try {
result = provider.authenticate(authentication);
if(result != null){
copyDetails(authentication, result);
break;
}
}
...
catch(AuthenticationException e){
lastException = e;
}
}
// 如果有 Authentication 信息,则直接返回
if(result != null){
if(eraseCredentialsAfterAuthentication
&&(result instanceof CredentialsContainer)){
// 移除密码
((CredentialsContainer)result).eraseCredentials();
}
// 发布登录成功事件
eventPublisher.publishAuthenticationSuccess(result);
return result;
}
...
// 执行到此,说明没有认证成功,包装异常信息
if(lastException == null){
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName()},
"No AuthenticationProvider found for {0}"));
}
prepareException(lastException, authentication);
throw lastException;
}
}
ProviderManager中的List,会依照次序去认证,认证成功则立即返回,若认证失败则返回null,下一个AuthenticationProvider会继续尝试认证,如果所有认证器都无法认证成功,则ProviderManager会抛出一个ProviderNotFoundException异常。
到这里,如果不纠结于AuthenticationProvider的实现细节以及安全相关的过滤器,认证相关的核心类其实都已经介绍完毕了:身份信息的存放容器SecurityContextHolder,身份信息的抽象Authentication,身份认证器AuthenticationManager及其认证流程。姑且在这里做一个分隔线。
6、AuthenticationProvider
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
- authenticate():认证方法
- 定义了认证的实现过程,它的参数Authentication里面包含了登录用户所提交的用户、密码等。返回值也是一个Authentication,这个Authentication则是在认证成功后,将用户的权限及其他信息重新组装后生成。
- SpringSecurity中维护着一个List列表,存放多种认证方式,不同的认证方式使用不同的AuthenticationProvider。如使用用户名密码登录时,使用AuthenticationProvider1,短信登录时使用AuthenticationProvider2等等这样的例子很多。
- supports():满足什么样的身份信息才进行如上认证
- 每个AuthenticationProvider需要实现supports()方法来表明自己支持的认证方式,如我们使用表单方式认证,在提交请求时SpringSecurity会生成UsernamePasswordAuthenticationToken,它是一个Authentication,里面封装着用户提交的用户名、密码信息。
- 可以自己实现AuthenticationProvider接口来自定义认证
6.1、实现类
- DaoAuthenticationProvider:从数据库中读取用户信息验证身份
- AnonymousAuthenticationProvider:匿名用户身份认证
- RememberMeAuthenticationProvider:已存cookie中的用户信息身份认证
- AuthByAdapterProvider:使用容器的适配器验证身份
- CasAuthenticationProvider:根据Yale中心认证服务验证身份,用于实现单点登陆
- JaasAuthenticationProvider:从JASS登陆配置中获取用户信息验证身份
- RemoteAuthenticationProvider:根据远程服务验证用户身份
- RunAsImplAuthenticationProvider:对身份已被管理器替换的用户进行验证
- X509AuthenticationProvider:从X509认证中获取用户信息验证身份
- TestingAuthenticationProvider:单元测试时使用
6.2、DaoAuthenticationProvider源码
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//密码加解密算法
private PasswordEncoder passwordEncoder;
//用户信息dao
private UserDetailsService userDetailsService;
//检查用户名和密码是否匹配
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if(authentication.getCredentials()== null){
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
//用户提交的密码凭证
String presentedPassword = authentication.getCredentials().toString();
//比较两个密码
if(!passwordEncoder.matches(presentedPassword, userDetails.getPassword())){
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
//获取用户信息
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
}
}
7、SecurityContext
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication var1);
}
- 安全上下文
- getAuthentication():获取上下文
8、总结
8.1、uml图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026134107963.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3U5MjA3ODYzMTI=,size_16,color_FFFFFF,t_70#pic_center)
8.2、整个认证过程
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026134119682.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3U5MjA3ODYzMTI=,size_16,color_FFFFFF,t_70#pic_center)