Spring Security实现基于RBAC的权限表达式动态访问控制
昨天有个粉丝加了我,问我如何实现类似shiro的资源权限表达式的访问控制。我以前有一个小框架用的就是shiro,权限控制就用了资源权限表达式,所以这个东西对我不陌生,但是在Spring Security中我并没有使用过它,不过我认为Spring Security可以实现这一点。是的,我找到了实现它的方法。
资源权限表达式
说了这么多,我觉得应该解释一下什么叫资源权限表达式。权限控制的核心就是清晰地表达出特定资源的某种操作,一个格式良好好的权限声明可以清晰表达出用户对该资源拥有的操作权限。
通常一个资源在系统中的标识是唯一的,比如User用来标识用户,ORDER标识订单。不管什么资源大都可以归纳出以下这几种操作
在 shiro权限声明通常对上面的这种资源操作关系用冒号分隔的方式进行表示。例如读取用户信息的操作表示为USER:READ
,甚至还可以更加细一些,用USER:READ:123
表示读取ID为123
的用户权限。
资源操作定义好了,再把它和角色关联起来不就是基于RBAC的权限资源控制了吗?就像下面这样:
这样资源和角色的关系可以进行CRUD操作进行动态绑定。
Spring Security中的实现
资源权限表达式动态权限控制在Spring Security也是可以实现的。首先开启方法级别的注解安全控制。
/**
* 开启方法安全注解
*
* @author felord.cn
*/
@EnableGlobalMethodSecurity(prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfig {
}
MethodSecurityExpressionHandler
MethodSecurityExpressionHandler
提供了一个对方法进行安全访问的门面扩展。它的实现类DefaultMethodSecurityExpressionHandler
更是提供了针对方法的一系列扩展接口,这里我总结了一下:
这里的PermissionEvaluator
正好可以满足需要。
PermissionEvaluator
PermissionEvaluator
接口抽象了对一个用户是否有权限访问一个特定的领域对象的评估过程。
public interface PermissionEvaluator extends AopInfrastructureBean {
boolean hasPermission(Authentication authentication,
Object targetDomainObject, Object permission);
boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission);
}
这两个方法仅仅参数列表不同,这些参数的含义为:
authentication
当前用户的认证信息,持有当前用户的角色权限。targetDomainObject
用户想要访问的目标领域对象,例如上面的USER
。permission
这个当前方法设定的目标领域对象的权限,例如上面的READ
。targetId
这种是对上面targetDomainObject
的具体化,比如ID为123
的USER
,我觉得还可以搞成租户什么的。targetType
是为了配合targetId
。
第一个方法是用来实现
USER:READ
的;第二个方法是用来实现USER:READ:123
的。
思路以及实现
targetDomainObject:permission
不就是USER:READ
的抽象吗?只要找出USER:READ
对应的角色集合,和当前用户持有的角色进行比对,它们存在交集就证明用户有权限访问。借着这个思路胖哥实现了一个PermissionEvaluator
:
/**
* 资源权限评估
*
* @author felord.cn
*/
public class ResourcePermissionEvaluator implements PermissionEvaluator {
private final BiFunction<String, String, Collection<? extends GrantedAuthority>> permissionFunction;
public ResourcePermissionEvaluator(BiFunction<String, String, Collection<? extends GrantedAuthority>> permissionFunction) {
this.permissionFunction = permissionFunction;
}
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
//查询方法标注对应的角色
Collection<? extends GrantedAuthority> resourceAuthorities = permissionFunction.apply((String) targetDomainObject, (String) permission);
// 用户对应的角色
Collection<? extends GrantedAuthority> userAuthorities = authentication.getAuthorities();
// 对比 true 就能访问 false 就不能访问
return userAuthorities.stream().anyMatch(resourceAuthorities::contains);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
//todo
System.out.println("targetId = " + targetId);
return true;
}
}
第二个方法没有实现,因为两个差不多,第二个你可以想想具体的使用场景。
配置和使用
PermissionEvaluator
需要注入到Spring IoC,并且Spring IoC只能有一个该类型的Bean:
@Bean
PermissionEvaluator resourcePermissionEvaluator() {
return new ResourcePermissionEvaluator((targetDomainObject, permission) -> {
//TODO 这里形式其实可以不固定
String key = targetDomainObject + ":" + permission;
//TODO 查询 key 和 authority 的关联关系
// 模拟 permission 关联角色 根据key 去查 grantedAuthorities
Set<SimpleGrantedAuthority> grantedAuthorities = new HashSet<>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return "USER:READ".equals(key) ? grantedAuthorities : new HashSet<>();
});
}
接下来写个接口,用@PreAuthorize
注解标记,然后直接用hasPermission('USER','READ')
来静态绑定该接口的访问权限表达式:
@GetMapping("/postfilter")
@PreAuthorize("hasPermission('USER','READ')")
public Collection<String> postfilter(){
List<String> list = new ArrayList<>();
list.add("felord.cn");
list.add("码农小胖哥");
list.add("请关注一下");
return list;
}
然后定义一个用户:
@Bean
UserDetailsService users() {
UserDetails user = User.builder()
.username("felord")
.password("123456")
.passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
.roles("USER")
.authorities("ROLE_ADMIN","ROLE_USER")
.build();
return new InMemoryUserDetailsManager(user);
}
接下来肯定是正常能够访问接口的。当你改变了@PreAuthorize
中表达式的值或者移除了用户的ROLE_ADMIN
权限,再或者USER:READ
关联到了其它角色等等,都会返回403
。
留给你去测试的
你可以看看注解改成这样会是什么效果:
@PreAuthorize("hasPermission('1234','USER','READ')")
还有这个:
@PreAuthorize("hasPermission('USER','READ') or hasRole('ADMIN')")
或者让targetId
动态化:
@PreAuthorize("hasPermission(#id,'USER','READ')")
public Collection<String> postfilter(String id){
}
关注公众号:Felordcn 获取更多资讯
Spring Security实现基于RBAC的权限表达式动态访问控制的更多相关文章
- Spring与Shiro整合 加载权限表达式
Spring与Shiro整合 加载权限表达式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 如何加载权限表达式 我们在上章内容中画了一张图,里面有三个分项,用户 角色 权限: 那 ...
- 基于RBAC实现权限管理
基于RBAC实现权限管理 技术栈:SpringBoot.SpringMVC RBAC RBAC数据库表 主体 编号 账号 密码 001 admin 123456 资源 编号 资源名称 访问路径 001 ...
- 基于RBAC的权限控制浅析(结合Spring Security)
嗯,昨天面试让讲我的项目,让我讲讲项目里权限控制那一块的,讲的很烂.所以整理一下. 按照面试官的提问流程来讲: 一.RBAC是个啥东西了? RBAC(Role-Based Access Control ...
- 基于Spring Security 的JSaaS应用的权限管理
1. 概述 权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源.资源包括访问的页面,访问的数据等,这在传统的应用系统中比较常见.本文介绍的则是基于Saas系统 ...
- spring security实现简单的url权限拦截
在一个系统中,权限的拦截是很常见的事情,通常情况下我们都是基于url进行拦截.那么在spring security中应该怎么配置呢. 大致步骤如下: 1.用户登录成功后我们需要拿到用户所拥有的权限,并 ...
- Spring Security构建Rest服务-1401-权限表达式
Spring Security 的权限表达式 用法,在自定义的BrowserSecurityConfig extends WebSecurityConfigurerAdapter 配置文件里,每一个a ...
- springboot整合security实现基于url的权限控制
权限控制基本上是任何一个web项目都要有的,为此spring为我们提供security模块来实现权限控制,网上找了很多资料,但是提供的demo代码都不能完全满足我的需求,因此自己整理了一版. 在上代码 ...
- spring security采用基于简单加密 token 的方法实现的remember me功能
记住我功能,相信大家在一些网站已经用过,一些安全要求不高的都可以使用这个功能,方便快捷. spring security针对该功能有两种实现方式,一种是简单的使用加密来保证基于 cookie 的 to ...
- ThinkPHP框架下基于RBAC的权限控制模式详解
这几天因为要做一个项目,需要可以对Web应用中通用功能进行封装,其中一个很重要的涉及到了对用户.角色和权限部分的灵活管理.所以基于TP框架自己封装了一个对操作权限和菜单权限进行灵活配置的可控制模式. ...
随机推荐
- 先知xss挑战赛学习笔记
xss游戏 游戏地址:http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/ LEMON参考wp地址 1. 文件上传 源码如下 <?php ...
- Myql 中的事务回滚机制概述 ?
事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个 不可分割的工作单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤 销. 要同时修改数据库中两个不同表时,如果它们不是一个事 ...
- Kafka 消费者是否可以消费指定分区消息?
Kafa consumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥 ...
- kafka中的broker 是干什么的?
broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一 ...
- 使用Pycharm获取Resources目录里的内容
def get_resource_path(path: str) -> str: """\ 获取Resources目录里的资源 :param path: :retu ...
- Auth0案例学习(SpringMVC形式)
官方SpringMVC示例Github:https://github.com/auth0-samples/auth0-spring-mvc-sample/tree/master/01-Login Ma ...
- @Autowired 注解 ?
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配. 它的用法和@Required 一样,修饰 setter 方法.构造器.属性或者具有任意名称 和/或多个参数的 PN 方 ...
- Zookeeper 下 Server 工作状态 ?
服务器具有四种状态,分别是 LOOKING.FOLLOWING.LEADING.OBSERVING. 1.LOOKING:寻找 Leader 状态.当服务器处于该状态时,它会认为当前集群中 没有 Le ...
- pandas学习总结
什么是pandas pandas数据读取 03. Pandas数据结构 Pandas查询数据的几种方法
- 安装TypeScript
安装TypeScript 创建工程文件夹:mkdir <project folder> 进入工程文件夹:cd <project folder> 快速创建程序包:npm init ...