经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
SpringCloud?微服务数据权限控制的实现
来源:jb51  时间:2021/11/24 19:10:17  对本文有异议

举个例子:

有一批业务员跟进全国的销售订单。他们被按城市进行划分,一个业务员跟进3个城市的订单,为了保护公司的业务数据不能被所有人都掌握,故每个业务员只能看到自己负责城市的订单数据。所以从系统来讲每个业务员都有访问销售订单的功能,然后再需要配置每个业务员负责的城市,以此对订单数据进行筛选。

要实现此功能有很多方法,如果系统中多个地方都需要类似的需求,那我们就可以将其提出来做成一个通用的功能。这里我介绍一个相对简单的解决方案,以供参考。

一、 整体架构

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

二、 实现流程

1.浏览器传带查询权限范围参数访问Controller,如cities

  1. POST http://127.0.0.1:8000/order/query
  2. accept: */*
  3. Content-Type: application/json
  4. token: 1e2b2298-8274-4599-a26f-a799167cc82f
  5.  
  6. {"cities":["cq","cd","bj"],"userName":"string"}

2.通过注解拦截权限范围参数,并根据预授权范围比较,回写在授权范围内的权限范围参数

  1. cities = ["cq","cd"]

3.通过参数传递到DAO层,在SQL语句中拼装出查询条件,实现数据的过滤

  1. select * from order where city in ('cq','cd')

三、 实现步骤

1. 注解实现

注解的完整代码,请详见源代码

1)创建注解

  1. @Retention(value = RetentionPolicy.RUNTIME)
  2. @Target(value = {ElementType.METHOD})
  3. @Documented
  4. public @interface ScopeAuth {
  5.  
  6. String token() default "AUTH_TOKEN";
  7. String scope() default "";
  8. String[] scopes() default {};
  9. }

此注解为运行时RetentionPolicy.RUNTIME作用在方法上ElementType.METHOD

token:获取识别唯一用户的标识,与用户数据权限存储有关

scopescopes:预请求的数据权限范围

2) AOP实现注解

  1. public class ScopeAuthAdvice {
  2. @Around("@annotation(scopeAuth)")
  3. public Object before(ProceedingJoinPoint thisJoinPoint, ScopeAuth scopeAuth) throws Throwable {
  4. // ... 省略过程
  5. // 获取token
  6. String authToken = getToken(args, scopeAuth.token(), methodSignature.getMethod());
  7. // 回写范围参数
  8. setScope(scopeAuth.scope(), methodSignature, args, authToken);
  9. return thisJoinPoint.proceed();
  10. }
  11.  
  12. /**
  13. * 设置范围
  14. */
  15. private void setScope(String scope, MethodSignature methodSignature, Object[] args, String authToken) {
  16. // 获取请求范围
  17. Set<String> requestScope = getRequestScope(args, scope, methodSignature.getMethod());
  18. ScopeAuthAdapter adapter = new ScopeAuthAdapter(supplier);
  19. // 已授权范围
  20. Set<String> authorizedScope = adapter.identifyPermissionScope(authToken, requestScope);
  21. // 回写新范围
  22. setRequestScope(args, scope, authorizedScope, methodSignature.getMethod());
  23. }
  24.  
  25. /**
  26. * 回写请求范围
  27. */
  28. private void setRequestScope(Object[] args, String scopeName, Collection<String> scopeValues, Method method) {
  29. // 解析 SPEL 表达式
  30. if (scopeName.indexOf(SPEL_FLAG) == 0) {
  31. ParseSPEL.setMethodValue(scopeName, scopeValues, method, args);
  32. }
  33. }
  34. }

此为演示代码省略了过程,主要功能为通过token拿到预先授权的数据范围,再与本次请求的范围做交集,最后回写回原参数。

过程中用到了较多的SPEL表达式,用于计算表达式结果,具体请参考ParseSPEL文件

3)权限范围交集计算

  1. public class ScopeAuthAdapter {
  2.  
  3. private final AuthQuerySupplier supplier;
  4.  
  5. public ScopeAuthAdapter(AuthQuerySupplier supplier) {
  6. this.supplier = supplier;
  7. }
  8.  
  9. /**
  10. * 验证权限范围
  11. * @param token
  12. * @param requestScope
  13. * @return
  14. */
  15. public Set<String> identifyPermissionScope(String token, Set<String> requestScope) {
  16. Set<String> authorizeScope = supplier.queryScope(token);
  17.  
  18. String ALL_SCOPE = "AUTH_ALL";
  19. String USER_ALL = "USER_ALL";
  20.  
  21. if (authorizeScope == null) {
  22. return null;
  23. }
  24.  
  25. if (authorizeScope.contains(ALL_SCOPE)) {
  26. // 如果是全开放则返回请求范围
  27. return requestScope;
  28. }
  29.  
  30. if (requestScope == null) {
  31. return null;
  32. }
  33.  
  34. if (requestScope.contains(USER_ALL)){
  35. // 所有授权的范围
  36. return authorizeScope;
  37. }
  38.  
  39. // 移除不同的元素
  40. requestScope.retainAll(authorizeScope);
  41.  
  42. return requestScope;
  43. }
  44. }

此处为了方便设置,有两个关键字范围

  • AUTH_ALL:预设所有范围,全开放的意思,为数据库预先设置值,请求传什么值都通过
  • USER_ALL:请求所有授权的范围,请求时传此值则会以数据库预设值为准

4) spring.factories自动导入类配置

  1. org.springframework.boot.autoconfigure.AutoConfigurationImportSelector= fun.barryhome.cloud.annotation.ScopeAuthAdvice

如果注解功能是单独项目存在,在使用时有可能会存在找不到引入文件的问题,可通过此配置文件自动载入需要初始化的类

2. 注解使用

  1. @ScopeAuth(scopes = {"#orderDTO.cities"}, token = "#request.getHeader(\"X-User-Name\")")
  2. @PostMapping(value = "/query")
  3. public String query(@RequestBody OrderDTO orderDTO, HttpServletRequest request) {
  4. return Arrays.toString(orderDTO.getCities());
  5. }

在需要使用数据权限的controller方法上增加@ScopeAuth注解

scopes = {"#orderDTO.cities"}:表示取输入参数orderDTO的cities值,这里是表达式必须加#

实际开发过程中,需要将orderDTO.getCities()带入后续逻辑中,在DAO层将此拼装在SQL中,以实现数据过滤功能

3. 实现AuthStoreSupplier

AuthStoreSupplier接口为数据权限的存储接口,与AuthQuerySupplier配合使用,可按实际情况实现

此接口为非必要接口,可由数据库或Redis存储(推荐),一般在登录的同时保存在Redis中

4. 实现AuthQuerySupplier

AuthQuerySupplier接口为数据权限查询接口,可按存储方法进行查询,推荐使用Redis

  1. @Component
  2. public class RedisAuthQuerySupplier implements AuthQuerySupplier {
  3.  
  4. @Autowired
  5. private RedisTemplate<String, String> redisTemplate;
  6.  
  7. /**
  8. * 查询范围
  9. */
  10. @Override
  11. public Set<String> queryScope(String key) {
  12. String AUTH_USER_KEY = "auth:logic:user:%s";
  13. String redisKey = String.format(AUTH_USER_KEY, key);
  14.  
  15. List<String> range = redisTemplate.opsForList().range(redisKey, 0, -1);
  16.  
  17. if (range != null) {
  18. return new HashSet<>(range);
  19. } else {
  20. return null;
  21. }
  22. }
  23. }

在分布式结构里,也可将此实现提出到权限模块,采用远程调用方式,进一步解耦

5. 开启数据权限

  1. @EnableScopeAuth
  2. @EnableDiscoveryClient
  3. @SpringBootApplication
  4. public class OrderApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(OrderApplication.class, args);
  7. }
  8. }

四、 综述

至此数据权限功能就实现了。在微服务器架构中为了实现功能的复用,将注解的创建和AuthQuerySupplier的实现提取到公共模块中,那么在具体的使用模块就简单得多了。只需增加@ScopeAuth注解,配置好查询方法就可以使用。

五、源代码

文中代码由于篇幅原因有一定省略并不是完整逻辑,如有兴趣请Fork源代码

到此这篇关于SpringCloud 微服务数据权限控制的实现的文章就介绍到这了,更多相关SpringCloud内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号