SpringSecurity专栏(Security中的权

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

写在前面

通过前面几讲的介绍,相信你已经对 Spring Security 中的认证流程有了更全面的了解。认证是实现授权的前提和基础

通常我们在执行授权操作时需要明确目标用户,只有明确目标用户才能明确它所具备的角色和权限,用户、角色和权限也是 Spring Security 中所采用的授权模型,所以今天我们来学习Security中的权限和角色。

多说一句 欢迎大家点击我头像查看Security专栏,设计模式专栏已完结。

Spring Security 中的权限和角色

我们可以回顾下,配置方法的处理过程位于 WebSecurityConfigurerAdapter 类中,但使用的是另一个 configure(HttpSecurity http) 方法,示例代码如下所示:

protected void configure(HttpSecurity http) throws Exception {
        http
           .authorizeRequests().anyRequest().authenticated()
           .and()
           .formLogin()
           .and()
           .httpBasic();
}
复制代码

这是 Spring Security 中作用于访问授权的默认实现方法。

基于权限进行访问控制

image.png

上图中的 GrantedAuthority 对象代表的就是一种权限对象,而一个 UserDetails 对象具备一个或多个 GrantedAuthority 对象。通过这种关联关系,实际上我们就可以对用户的权限做一些限制,如下所示:

image.png

如果用代码来表示这种关联关系,可以采用如下所示的实现方法:

UserDetails user = User.withUsername("yn")
     .password("123456")
     .authorities("create", "delete")
     .build();
复制代码

可以看到,这里我们创建了一个名为“jianxiang”的用户,该用户具有“create”和“delete”这两个权限。在 Spring Security 中,提供了一组针对 GrantedAuthority 的配置方法。例如:

  • hasAuthority(String),允许具有特定权限的用户进行访问;

  • hasAnyAuthority(String),允许具有任一权限的用户进行访问。

可以使用上述两个方法来判断用户是否具备对应的访问权限,我们在WebSecurityConfigurerAdapter 的 configure 方法中添加如下代码:

@Override
protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic();
    http.authorizeRequests().anyRequest().hasAuthority("CREATE");        
}
复制代码

这段代码的作用是对于任何请求,只有权限为“CREATE”才能采用访问。如果我们修改一下代码:

http.authorizeRequests().anyRequest().hasAnyAuthority("CREATE", "DELETE");
复制代码

此时,只要具备“CREATE”和“DELETE”中任意一种权限的用户都能进行访问。

这两个方法实现起来都比较简单,但局限性也很大,因为我们无法基于一些来自环境和业务的参数灵活控制访问规则。为此,Spring Security 还提供了一个 access() 方法,该方法允许开发人员传入一个表达式进行更加细粒度的权限控制。

这里,我们将引入 SpEL,它是 Spring Expression Language 的简称,是 Spring 框架提供的一种动态表达式语言。基于 SpEL,只要该表达式的返回值是 true,access() 方法就会允许用户访问。如下示例:

http.authorizeRequests().anyRequest().access("hasAuthority('CREATE')");
复制代码

上述代码与使用 hasAuthority() 方法的效果是完全一致的,但如果是更为复杂的场景,access() 方法的优势就很明显了。我们可以灵活创建一个表达式,然后通过 access() 方法确定最后的结果,示例代码如下所示:

String expression = "hasAuthority('CREATE') and !hasAuthority('Retrieve')"; 
http.authorizeRequests().anyRequest().access(expression);
复制代码

上述代码的效果是只有拥有“CREATE”权限且不拥有“Retrieve”权限的用户才能进行访问。

基于角色进行访问控制

讨论完权限,我们再来看角色,你可以把角色看成是拥有多个权限的一种数据载体,如下图所示,这里我们分别定义了两个不同的角色“User”和“Admin”,它们拥有不同的权限:

image.png

讲到这里,你可能会认为 Spring Security 应该提供了一个独立的数据结构来承载角色的含义。但事实上,在 Spring Security 中,并没有定义类似“GrantedRole”这种专门用来定义用户角色的对象,而是复用了 GrantedAuthority 对象。事实上,以“ROLE_”为前缀的 GrantedAuthority 就代表了一种角色,因此我们可以使用如下方式初始化用户的角色:

UserDetails user = User.withUsername("yn")
      .password("123456")
      .authorities("ROLE_ADMIN")
      .build();
复制代码

上述代码相当于为用户“jianxiang”指定了“ADMIN”这个角色。为了给开发人员提供更好的开发体验,Spring Security 还提供了另一种简化的方法来指定用户的角色,如下所示

UserDetails user = User.withUsername("yn")
      .password("123456")
      .roles("ADMIN")
      .build();
复制代码

和权限配置一样,Spring Security 也通过使用对应的 hasRole() 和 hasAnyRole() 方法来判断用户是否具有某个角色或某些角色,使用方法如下所示:

http.authorizeRequests().anyRequest().hasRole("ADMIN");
复制代码

当然,针对角色,我们也可以使用 access() 方法完成更为复杂的访问控制。而 Spring Security 还提供了其他很多有用的控制方法供开发人员进行灵活使用。作为总结,下表展示了常见的配置方法及其作用:

配置方法 作用
anonymous() 允许匿名访问
authenticated() 允许认证用户访问
denyAll() 无条件禁止一切访问
hasAnyAuthority(String) 允许具有任一权限的用户进行访问
hasAnyRole(String) 允许具有任一角色的用户进行访问
hasAuthority(String) 允许具有特定权限的用户进行访问
hasIpAddress(String) 允许来自特定 IP 地址的用户进行访问
hasRole(String) 允许具有特定角色的用户进行访问
permitAll() 无条件允许一切访问

Spring Security 中的配置方法列表

好了,今天Security 中的权限和角色就讲到这里。

总结

这一讲我们关注的是对请求访问进行授权,而这个过程需要明确 Spring Security 中的用户、权限和角色之间的关联关系。一旦我们对某个用户设置了对应的权限和角色,那么就可以通过各种配置方法来有效控制访问权限。下期我们讲权限的匹配器,下期见,加油。

弦外之音

感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。也欢迎有问题我们下面评论交流

加油! 我们下期再见!

给大家分享几个我前面写的几篇骚操作

聊聊不一样的策略模式(值得收藏)

copy对象,这个操作有点骚!