第五章 SpringSecurity 中的核心组件
通过前四章简单介绍了下 SpringSecurity 的命名空间配置和Java 配置,已经可以简单一个基于 Spring Security 的应用程序。
下面,将会研究其中一些在整个框架中使用的中心接口、类和抽象概念,了解它们如何协同工作,以支持 Spring Security 中的身份验证和访问控制。
一、SecurityContextHolder
Spring-Security-Core 中对 SecurityContextHolder 的介绍如下:
Associates a given {@link SecurityContext} with the current execution thread.
将给定的{@link SecurityContext}与当前执行线程关联。
SecurityContextHolder 用于存储应用程序当前Security 上下文的地方,其中包含当前使用应用程序的主体的细节。
默认情况下,SecurityContextHolder 使用ThreadLocal 来存储这些细节,如果在处理完当前主体的请求之后清除线程,那么这种方式就是非常安全的。
一般没有必要修改SecurityContextHolder 的默认值,但是 SpringSecurity 提供了修改的途径。
二、SecurityContext
Interface defining the minimum security information associated with the current thread of execution.
与安全信息相关的抽象接口,用来获取或设置 Authentication
。
三、Authentication
实现了 Principal
与序列化的接口,包含了当前请求的主体信息。通常是保存在 SecurityContext
中。
获取方法如下:
public String getUsernameBySecurityContext(){
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username;
if(prinicpal instanceof UserDetails){
username = ((UserDetails)principal).getUsername();
}else {
username = principal.toString();
}
return username;
}
四、GrantedAuthority
除了用户主体之外,身份验证还提供了一额外的方法 getAuthorities(),该方法返回一个 GrantedAuthority 对象数组。
GrantedAuthority 通常为“角色”,用于配置 web授权、方法授权、域对象授权等。该属性通常由 UserDetailsService 加载给 UserDetails。
如果一个用户有几千个这种权限,内存的消耗将会是非常巨大的。
五、UserDetails
从上面代码可以看出 Authentication
在大部分情况下是可以直接转换为 UserDetails
对象的。他表示一个主体,可以看做是用户数据库与 SecurityContextHolder 之间的适配器。
UserDetails 是 SpringSecurity 的核心接口,可以通过继承 UserDetails 来实现自定义的方法与属性。
扩展UserDetails
通过自定义 UserDetails 的实现,我们可以将其作为数据库实体与Authentication 的适配者。
数据库实体 User:
@Data
@Document(collection = "db_user")
class User{
@Id
private String id;
private String username;
private String password;
private String[] roles;
}
适配器实体 CustomUserDetails:
@EqualsAndHashCode(callSuper = true)
@Data
public class CustomUserDetails extends User implements UserDetails {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList(super.getRoles());
}
@Override
public String getPassword() {
return super.getPassword();
}
@Override
public String getUsername() {
return super.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
这样,我们就可以通过 CustomUserDetails 在 User 与 Authentication 之间相互转换,实现数据库认证 SpringSecurity。
那么 UserDetails 对象是从哪里创建的呢?就是下面的 UserDetailsService 类。
六、UserDetailsService
UserDetailsService 只有一个特殊方法,接收一个 string 的用户名参数,并返回一个 UserDetails。
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
通过自定义实现 UserDetailsService,返回自己的 CustomUserDetails 对象。
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if(user == null)
throw new UsernameNotFoundException("user not found");
final CustomUserDetails userDetails = new CustomUserDetails();
BeanUtils.copyProperties(user,userDetails);
// todo 一些其他附加属性等
return userDetails;
}
}