springSecurity简介
Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案,是一种基于Spring AOP和Servlet规范中的Filter实现的安全框架,其本质是一个过滤器链
名词解释
**主体(principal)**使用系统的用户或设备或从其他系统远程登录的用户等等,谁在使用该系统
**认证(authentication)**权限管理系统确认一个主体的身份,允许主体进入系统,“主体”证明自己是谁
**授权(authorization)**将操作系统的“权力”授予“主体”,这样主体就具备了操作系统中特定功能的能力,授权就是给用户分配权限
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
核心类
SecurityContextHolder 用于存储安全上下文的信息,当前用户、是否被认证、权限都存储在其中
提供了三种存储策略,当然也可以自定义储存策略,设置strategyName来进行反射获取
- MODE_THREADLOCAL 使用ThreadLocalSecurityContextHolderStrategy,默认
- MODE_INHERITABLETHREADLOCAL 使用InheritableThreadLocalSecurityContextHolderStrategy,子线程可以使用父线程中存放的变量
- MODE_GLOBAL 使用GlobalSecurityContextHolderStrategy,全局使用同一个SecurityContext
SecurityContextHolder.getContext()即为SecurityContext,其中保存着当前正在访问系统的用户详细信息,默认使用的是ThreadLocal来保存SecurityContext,在每次request结束后都会自动清除当前线程的ThreadLocal
Authentication 继承自Principal,可以通过该接口来获取到用户的权限、密码以及身份信息和认证信息,登录成功后会保存在SecurityContextHolder.getContext()中
SecurityContextHolder.getContext().getAuthentication()
- getAuthorities 获取权限
- getCredentials 获取密码,用户提交的密码
- getDetails 获取详情
- getPrincipal 获取认证信息,返回的是UserDetails的实现类
AuthenticationManager 认证相关的核心接口,定义了认证方法authenticate
// 接收认证请求Authentication,如果认证成功,则返回一个封装了当前用户权限等信息的Authentication对象 Authentication authenticate(Authentication authentication) throws AuthenticationException;
ProviderManager 认证管理,实现了AuthenticationManager,是默认实现。其内部是一个AuthenticationProvider列表,并在authenticate方法中将真正的认证逻辑委托给了AuthenticationProvider对象
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 配置自定义的用户服务,查找用户 // authenticationProvider是进行this.authenticationProviders.add(authenticationProvider);将AuthenticationProvider加到列表中 auth.authenticationProvider(authenticationProvider()); }
DaoAuthenticationProvider 该类是AuthenticationProvider最常用的实现类,使用UserDetailsService来加载UserDetails,用来获取用户提交的用户名和密码对比正确性,实现了其父类的两个抽象方法,通过该类来进行认证用户
// 用于身份认证 protected abstract void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) // 用于从数据库获取用户信息, 这里会调用userDetailsService.loadUserByUsername方法来进行获取 protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;
UserDetails 用户信息
public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); // 正确的密码 String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
UserDetailsService 负责获取正确的用户信息,认证时会通过UserDetailsService.loadUserByUsername来获取到对应的UserDetails进行认证。认证通过后将该UserDetails赋给Authentication的principal,然后再把Authentication存入SecurityContext中。提供了几个实现类,也可以实现UserDetailsService接口来进行自定义。
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
GrantedAuthority 表示权限,Authentication.getAuthorities可以获取当前Authentication的权限,是一个GrantedAuthority数组
进行认证
UsernamePasswordAuthenticationFilter
对/login 的 POST 请求做拦截,校验表单中用户名,密码
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 用户名和密码被过滤器获取到,封装成 Authentication,通常情况下是 UsernamePasswordAuthenticationToken这个实现类
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
// AuthenticationManager 身份管理器负责验证这个 Authentication
return this.getAuthenticationManager().authenticate(authRequest);
}
# 配置用户名,密码
security:
user:
name: admin
password: admin
其中的用户名密码都是spring security生成或者配置的,但是在实际使用中用户名和密码都是从数据库去进行读取的,所以这段逻辑需要进行自定义,如果需要自定义逻辑时,需要实现 UserDetailsService 接口
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
https://zhhll.icu/2021/框架/springSecurity/1.springSecurity简介/