SpringCloud|SpringCloud 微服务数据权限控制的实现
目录
- 一、 整体架构
- 二、 实现流程
- 三、 实现步骤
- 1. 注解实现
- 2. 注解使用
- 3. 实现AuthStoreSupplier
- 4. 实现AuthQuerySupplier
- 5. 开启数据权限
- 四、 综述
- 五、源代码
有一批业务员跟进全国的销售订单。他们被按城市进行划分,一个业务员跟进3个城市的订单,为了保护公司的业务数据不能被所有人都掌握,故每个业务员只能看到自己负责城市的订单数据。所以从系统来讲每个业务员都有访问销售订单的功能,然后再需要配置每个业务员负责的城市,以此对订单数据进行筛选。
要实现此功能有很多方法,如果系统中多个地方都需要类似的需求,那我们就可以将其提出来做成一个通用的功能。这里我介绍一个相对简单的解决方案,以供参考。
一、 整体架构

文章图片
数据权限为作一个注解的形式挂在每一个需要数据权限控制的Controller上,由于和具体的程序逻辑有关故有一定的入侵性,且需要数据库配合使用。
二、 实现流程

文章图片
1.浏览器传带查询权限范围参数访问Controller,如cities
POST http://127.0.0.1:8000/order/queryaccept: */*Content-Type: application/jsontoken: 1e2b2298-8274-4599-a26f-a799167cc82f{"cities":["cq","cd","bj"],"userName":"string"}
2.通过注解拦截权限范围参数,并根据预授权范围比较,回写在授权范围内的权限范围参数
cities = ["cq","cd"]
3.通过参数传递到DAO层,在SQL语句中拼装出查询条件,实现数据的过滤
select * from order where city in ('cq','cd')
三、 实现步骤
1. 注解实现
注解的完整代码,请详见源代码
1)创建注解
@Retention(value = https://www.it610.com/article/RetentionPolicy.RUNTIME)@Target(value = {ElementType.METHOD})@Documentedpublic @interface ScopeAuth {String token() default"AUTH_TOKEN"; String scope() default ""; String[] scopes() default {}; }
【SpringCloud|SpringCloud 微服务数据权限控制的实现】此注解为运行时
RetentionPolicy.RUNTIME
作用在方法上ElementType.METHOD
的token
:获取识别唯一用户的标识,与用户数据权限存储有关scope
,scopes
:预请求的数据权限范围2) AOP实现注解
public class ScopeAuthAdvice {@Around("@annotation(scopeAuth)")public Object before(ProceedingJoinPoint thisJoinPoint, ScopeAuth scopeAuth) throws Throwable {// ... 省略过程// 获取tokenString authToken = getToken(args, scopeAuth.token(), methodSignature.getMethod()); // 回写范围参数setScope(scopeAuth.scope(), methodSignature, args, authToken); return thisJoinPoint.proceed(); }/*** 设置范围*/private void setScope(String scope, MethodSignature methodSignature, Object[] args, String authToken) {// 获取请求范围Set requestScope = getRequestScope(args, scope, methodSignature.getMethod()); ScopeAuthAdapter adapter = new ScopeAuthAdapter(supplier); // 已授权范围Set authorizedScope = adapter.identifyPermissionScope(authToken, requestScope); // 回写新范围setRequestScope(args, scope, authorizedScope, methodSignature.getMethod()); }/*** 回写请求范围*/private void setRequestScope(Object[] args, String scopeName, Collection scopeValues, Method method) {// 解析 SPEL 表达式if (scopeName.indexOf(SPEL_FLAG) == 0) {ParseSPEL.setMethodValue(scopeName, scopeValues, method, args); }}}
此为演示代码省略了过程,主要功能为通过token拿到预先授权的数据范围,再与本次请求的范围做交集,最后回写回原参数。
过程中用到了较多的SPEL表达式,用于计算表达式结果,具体请参考ParseSPEL文件
3)权限范围交集计算
public class ScopeAuthAdapter {private final AuthQuerySupplier supplier; public ScopeAuthAdapter(AuthQuerySupplier supplier) {this.supplier = supplier; }/*** 验证权限范围* @param token* @param requestScope* @return*/public Set identifyPermissionScope(String token, Set requestScope) {Set authorizeScope = supplier.queryScope(token); String ALL_SCOPE = "AUTH_ALL"; String USER_ALL = "USER_ALL"; if (authorizeScope == null) {return null; }if (authorizeScope.contains(ALL_SCOPE)) {// 如果是全开放则返回请求范围return requestScope; }if (requestScope == null) {return null; }if (requestScope.contains(USER_ALL)){// 所有授权的范围return authorizeScope; }// 移除不同的元素requestScope.retainAll(authorizeScope); return requestScope; }}
此处为了方便设置,有两个关键字范围
AUTH_ALL
:预设所有范围,全开放的意思,为数据库预先设置值,请求传什么值都通过USER_ALL
:请求所有授权的范围,请求时传此值则会以数据库预设值为准
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector=\fun.barryhome.cloud.annotation.ScopeAuthAdvice
如果注解功能是单独项目存在,在使用时有可能会存在找不到引入文件的问题,可通过此配置文件自动载入需要初始化的类
2. 注解使用
@ScopeAuth(scopes = {"#orderDTO.cities"}, token = "#request.getHeader(\"X-User-Name\")")@PostMapping(value = "https://www.it610.com/query")public String query(@RequestBody OrderDTO orderDTO, HttpServletRequest request) {return Arrays.toString(orderDTO.getCities()); }
在需要使用数据权限的controller方法上增加@ScopeAuth注解
scopes = {"#orderDTO.cities"}
:表示取输入参数orderDTO的cities值,这里是表达式必须加#实际开发过程中,需要将orderDTO.getCities()带入后续逻辑中,在DAO层将此拼装在SQL中,以实现数据过滤功能
3. 实现AuthStoreSupplier
AuthStoreSupplier
接口为数据权限的存储接口,与AuthQuerySupplier配合使用,可按实际情况实现此接口为非必要接口,可由数据库或Redis存储(推荐),一般在登录的同时保存在Redis中
4. 实现AuthQuerySupplier
AuthQuerySupplier
接口为数据权限查询接口,可按存储方法进行查询,推荐使用Redis@Componentpublic class RedisAuthQuerySupplier implements AuthQuerySupplier {@Autowiredprivate RedisTemplate redisTemplate; /*** 查询范围*/@Overridepublic Set queryScope(String key) {String AUTH_USER_KEY = "auth:logic:user:%s"; String redisKey = String.format(AUTH_USER_KEY, key); List range = redisTemplate.opsForList().range(redisKey, 0, -1); if (range != null) {return new HashSet<>(range); } else {return null; }}}
在分布式结构里,也可将此实现提出到权限模块,采用远程调用方式,进一步解耦
5. 开启数据权限
@EnableScopeAuth@EnableDiscoveryClient@SpringBootApplicationpublic class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args); }}
四、 综述 至此数据权限功能就实现了。在微服务器架构中为了实现功能的复用,将注解的创建和AuthQuerySupplier的实现提取到公共模块中,那么在具体的使用模块就简单得多了。只需增加
@ScopeAuth
注解,配置好查询方法就可以使用。五、源代码 文中代码由于篇幅原因有一定省略并不是完整逻辑,如有兴趣请Fork源代码
到此这篇关于SpringCloud 微服务数据权限控制的实现的文章就介绍到这了,更多相关SpringCloud内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- 基于微信小程序带后端ssm接口小区物业管理平台设计
- CET4听力微技能一
- 微习惯复盘
- 社保代缴公司服务费包含哪些
- 员工的微信朋友圈是公司的宣传阵地吗()
- 私有化轻量级持续集成部署方案--03-部署web服务(下)
- FBI怎么和恐怖分子谈判
- 探索免费开源服务器tomcat的魅力
- 微雨中的东湖
- 小醉微记|小醉微记|心不安处皆动荡