场景:
- 用户通过提交username和password请求登陆
- 服务器验证身份信息是否正确
- 获取用户信息(包括角色集合)
- 利用3中的信息构建Security Context
- 在此之后,该用户的所有请求,Spring Security的访问控制机制将根据Security Context中的信息判断用户是否具有权限
以上,前3点即为认证,构建Security Context,为之后5的授权(访问控制)铺垫,SecurityContext中的用户信息也可供应用程序使用。
Spring Security的几个核心组件
1、SecurityContextHolder
用于存储的Security Context,主要是当前会话的Principal
,默认是用ThreadLocal存储这些信息;
常用于获取当前用户的相关信息,Spring Security使用Authentication
类来封装这些信息,获取当前会话用户的用户名代码如下所示:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
代码中的getContext()
方法的返回值是一个SecurityContext
的实例,就是在完成认证之后存储在ThreadLocal中的对象,主要封装了用户的相关信息。
2、UserDetails
、Principal
、Authentication
Spring Security中使用Authentication
存储当前用户的主要信息。
Authentication
中存储的是Principal
,与UserDetails
直接可以 强制转换。
UserDetails
是Spring Security中的一个核心接口,作用是充当具体业务逻辑中的用户对象与Spring Security中所需要的Principal
的适配器。
UserDetails
的源码如下所示:
public abstract interface UserDetails extends Serializable {
public abstract Collection<? extends GrantedAuthority> getAuthorities();
public abstract String getPassword();
public abstract String getUsername();
public abstract boolean isAccountNonExpired();
public abstract boolean isAccountNonLocked();
public abstract boolean isCredentialsNonExpired();
public abstract boolean isEnabled();
}
由源码可知其主要是用户名、密码、权限列表等信息;因此,通常可以在项目定义个自己的用户对象并实现该接口,重写其getAuthorities()方法即可。目的是为了让项目中具体业务逻辑中的权限信息与Spring Security所需的权限配置对应上。举例部分代码,省略getter与setter方法:
public class UserPo implements UserDetails{
private String userNo;
private String username;
private String password;
private List<RolePo> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
List<RolePo> roles = this.getRoles();
for(RolePo rp : roles){
auths.add(new SimpleGrantedAuthority(rp.getRoleName()));
}
return auths;
}
}
Spring Security提供一个UserDetailsService
接口,该接口用于查询并返回一个UserDetails
。只要我们实现了该接口,并注入到某