小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
写在前面
通过前面几讲的介绍,相信你已经对 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 中作用于访问授权的默认实现方法。
基于权限进行访问控制
上图中的 GrantedAuthority 对象代表的就是一种权限对象,而一个 UserDetails 对象具备一个或多个 GrantedAuthority 对象。通过这种关联关系,实际上我们就可以对用户的权限做一些限制,如下所示:
如果用代码来表示这种关联关系,可以采用如下所示的实现方法:
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”,它们拥有不同的权限:
讲到这里,你可能会认为 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 中的用户、权限和角色之间的关联关系。一旦我们对某个用户设置了对应的权限和角色,那么就可以通过各种配置方法来有效控制访问权限。下期我们讲权限的匹配器,下期见,加油。
弦外之音
感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。也欢迎有问题我们下面评论交流
加油! 我们下期再见!
给大家分享几个我前面写的几篇骚操作