上一篇有点水了,这期我们直接上干货,学习认证流程。

先来考虑一下,关于SpringSecurity如何实现的安全权限管理,角色校验,来对比一下权限和角色

先来考虑一下,关于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管理,这个思路应该没有什么毛病了。