上一篇有点水了,这期我们直接上干货,学习认证流程。
先来考虑一下,关于SpringSecurity
如何实现的安全权限管理,角色校验,来对比一下权限和角色
// 验证角色 访问 "/product/add" 所应该拥有 admin 的角色
.antMatchers("/product/add").hasRole("admin");
// 访问 "/product/add" 所应该拥有 PRODUCT_ADD 的权限
.antMatchers("/product/add").hasAnyAuthority("PRODUCT_ADD")
SpringSecurity
验证当前的用户是否具有这样的角色或者权限,但是这限制于当前的项目需要是前后端同时的情况,我们只需要将当前的权限设置给SpringSecurity
,例如:
/**
* 查询权限并将权限放入 security 中
*
* @param http
* @throws Exception
*/
public void selectPurview(HttpSecurity http) throws Exception {
List<Purview> purviews = purviewService.selectPurview();
for (Purview purview : purviews) {
http.authorizeRequests()
// 设置权限
.antMatchers(purview.getUrl()).hasAnyAuthority(purview.getAuthority());
}
}
如果你想设置成角色,那就将设置权限的代码改成设置角色即可,简单的东西就不上代码了,我们还是抓紧来看下在前后端分离的情况下,怎么处理。
谁开发的,看谁的讲解,直接看 SpringSecurity官方文档第10章文档,第一眼看上去肯定是 SecurityContextHolder
,这个类存储经过身份验证的人员的详细信息,讲的真的是很模糊,其实就是登录过后的人员信息。
下面就是SecurityContext
,可以从中获取,SecurityContextHolder
并包含Authentication
当前经过身份验证的用户的信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
AuthenticationManager
认证管理器就很强, 可以提供用户提供的用于身份验证的凭据,也可以提供来自的当前用户SecurityContext
,已经进行过身份验证的用户,Authentication
可以从SecurityContext
获取。
该Authentication
包含:
principal
-识别用户。使用用户名/密码进行身份验证时,通常是的一个实例UserDetails
。credentials
-通常是密码。在许多情况下,将在验证用户身份后清除此内容,以确保它不会泄漏。authorities
-在GrantedAuthoritys
是用户被授予高级别权限。角色或范围是几个例子。
这样的话我们可以猜测一下,如果我们将登录成功的用户信息封装成toekn
,当他请求接口时通过拦截器,将他的信息进行解析,再传递给 SecurityContextHolder
,那是不是就完成了校验。比如这样:
SecurityContextHolder.getContext().setAuthentication(Authentication authentication);
值得思考的是,如何构建这样的一个Authentication authentication
对象,回头再去看官方文档10.1,官方文档中提到:
创建一个新
Authentication
对象。Spring Security
并不关心Authentication
在上设置了什么类型的实现SecurityContext
。在这里我们使用TestingAuthenticationToken
它是因为它非常简单。更常见的生产方案是UsernamePasswordAuthenticationToken(userDetails, password, authorities)
。
UsernamePasswordAuthenticationToken
设计用于简单呈现用户名和密码的实现。查看源码:
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken;
public abstract class AbstractAuthenticationToken implements Authentication,CredentialsContainer;
这下就不难明白为什么UsernamePasswordAuthenticationToken
可以构建出一个Authentication
对象了。
private final Object principal;
private Object credentials;
/**
* This constructor should only be used by <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied with
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* authentication token.
*
* @param principal
* @param credentials
* @param authorities
*/
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
那就是说实际上,我们应该使用这个构建函数去构建Authentication
对象,也就是UsernamePasswordAuthenticationToken(principal, credentials, authorities)
详细解释一下这三个参数
principal
显然这个使用final
修饰不可以修改,所以传递的值一定是在认证之后不需要修改的,例如:用户信息credentials
用于防止认证的信息,可以是token
authorities
权限集合
思路好像又清晰了,认证的过程是这样的,我们进行登录认证,验证账号密码,生成TOEKN
,然后解析当前的TOEKN
,获取用户信息,权限集合,用于生成Authentication
,放在SecurityContext
中,将权限校验和验证都交由SpringSecurity
管理,这个思路应该没有什么毛病了。