1.背景

对于仓库过程流程,因为存在职能不同的多工种,多岗位,对于数据权限的要求也不相同。因此急需一套数据权限系统来保证业务流程正确性和数据安全性。

2.设计思路

数据权限系统,归根结底,是在对数据的CRUD操作时,对指定数据库中的指定表指定字段通过sql拦截的方式添加过滤条件。

在添加过滤条件之前,首先需要理解4个数据根源问题。

  1. 权限组(操作人对应的操作权限)
  2. 服务(权限组对应哪些服务)
  3. 表权限(服务中操作权限对应的数据权限)
  4. 字段权限(服务中数据权限对应的操作表字段)

权限组/服务/表/字段关系

5.数据权限系统 - 图1

人/岗位/权限组

5.数据权限系统 - 图2

3.功能实现

功能依赖如下组件

  • xy-redis-starter
  • xy-zookeeper-starter

5.数据权限系统 - 图3

1.服务启动注册数据权限定义(表,字段,字段类型)

2.服务实现数据权限中心提供的接口(泛化调用)

3.数据中心通过泛化调用创建权限组信息

4.数据中心绑定岗位与权限组关联关系

5.数据中心绑定员工与岗位关系(一对多)

6.服务调用权限中心接口获取用户当前服务权限数据

7.框架自动对删,改,查操作进行sql条件改写

画板

4.配置细节

1.配置数据权限元数据

  1. @Data
  2. @TableName("business_line")
  3. @DataScope(tableId = "business_line", desc = "用户",
  4. dataFields = {
  5. @DataField(fieldCode = "id", fieldDesc = "业务线Id", fieldType = FileTypeEnum.Long),
  6. @DataField(fieldCode = "status", fieldDesc = "状态", fieldType = FileTypeEnum.Integer)})
  7. public class BusinessLine {}

目前支持String,Long,Integer类型的字段过滤

2.引入permission-api

  1. <dependency>
  2. <groupId>com.trendsi</groupId>
  3. <artifactId>trendsi-permission-api</artifactId>
  4. <version>${permission-api.version}</version>
  5. </dependency>

3.实现泛化接口提供数据

  1. @Slf4j
  2. @DubboService(version = "1.0.0", group = "demo")
  3. public class DataScopeApiImpl implements DataScopeApi {
  4. @Override
  5. public DataScopeInfo getScopeInitData() {
  6. DataScopeInfo dataScopeInfo = new DataScopeInfo();
  7. dataScopeInfo.setSystemCode("demo");
  8. List<TableFieldData> tableScopeInfos = new ArrayList<>();
  9. TableFieldData tableScopeInfo1 = new TableFieldData("user", "用户信息", "dept_id", "部门ID", "1001", "产品部门");
  10. TableFieldData tableScopeInfo2 = new TableFieldData("user", "用户信息", "dept_id", "部门ID", "1002", "研发部门");
  11. tableScopeInfos.add(tableScopeInfo1);
  12. tableScopeInfos.add(tableScopeInfo2);
  13. dataScopeInfo.setTableScopeInfos(tableScopeInfos);
  14. }
  15. }

version:版本固定1.0.0

group:当前服务的spring.application.name

4.在数据权限中心配置服务信息

5.数据权限系统 - 图5

后台服务表示后端提供的微服务

前段页面表示web页面

5.配置权限组

5.数据权限系统 - 图6

6.权限组绑定数据权限

5.数据权限系统 - 图7

1标识当前服务的那些表配置了数据权限

2标识当前表的哪些字段配置了权限数据

3.标识当前字段的值

5.权限字段代码生成

  1. @Test
  2. public void generatorDataScope() {
  3. List<TableFieldData> list = new ArrayList<>();
  4. // 要扫描的包
  5. String packageName = "com.trendsi.permission";
  6. Reflections f = new Reflections(packageName);
  7. // 获取扫描到的标记注解的集合
  8. Set<Class<?>> set = f.getTypesAnnotatedWith(DataScope.class);
  9. System.err.println("List<TableFieldData> tableScopeInfos = new ArrayList<>();");
  10. //InterceptorIgnore
  11. for (Class<?> c : set) {
  12. // 循环获取标记的注解
  13. DataScope dataScope = c.getAnnotation(DataScope.class);
  14. String tableId = dataScope.tableId();
  15. String desc = dataScope.desc();
  16. DataField[] dataFields = dataScope.dataFields();
  17. for (DataField dataField : dataFields) {
  18. String fieldCode = dataField.fieldCode();
  19. String fieldDesc = dataField.fieldDesc();
  20. TableFieldData data = new TableFieldData();
  21. data.setTableCode(tableId);
  22. data.setTableDesc(desc);
  23. data.setFieldCode(fieldCode);
  24. data.setFieldDesc(fieldDesc);
  25. data.setFieldValue("");
  26. data.setFieldValueDesc("");
  27. list.add(data);
  28. }
  29. }
  30. if (CollectionUtils.isEmpty(list)) {
  31. return;
  32. }
  33. Set<String> fieldCodeSet = list.stream().map(TableFieldData::getFieldCode).collect(Collectors.toSet());
  34. Iterator<String> iterator = fieldCodeSet.iterator();
  35. while (iterator.hasNext()) {
  36. String fieldCode = iterator.next();
  37. String mark = MessageFormat.format("//获取{0}的值和描述列表", fieldCode);
  38. System.err.println(mark);
  39. List<TableFieldData> fieldDataList = list.stream().filter(s -> XyStringUtils.equals(s.getFieldCode(), fieldCode)).collect(Collectors.toList());
  40. System.err.println("for (Object data : dataList ) {");
  41. for (TableFieldData tableFieldData : fieldDataList) {
  42. String format = MessageFormat.format("TableFieldData {0} = new TableFieldData(\"{1}\", \"{2}\", \"{3}\", \"{4}\", \"obj.data\", \"obj.dataDesc\");",
  43. tableFieldData.getTableCode(),
  44. tableFieldData.getTableCode(),
  45. tableFieldData.getTableDesc(),
  46. tableFieldData.getFieldCode(),
  47. tableFieldData.getFieldDesc());
  48. System.err.println(format);
  49. System.err.println("tableScopeInfos.add(" + tableFieldData.getTableCode() + ");");
  50. }
  51. System.err.println("}");
  52. }
  53. }

6.遗留问题

1.本次缓存同步

用户的权限数据存在本地缓存中,默认有效10分钟

若用户权限发生变更,如何实时更新用户的权限(本地缓存同步问题)

思路1:zk监听

5.数据权限系统 - 图8

思路2:mq舰艇

思路3:redis的发布订阅

2.新增是否过滤

例如:存在1,2,3,4四个仓库,员工A只有仓库1,2的数据权限,那么在A新增数据时,是否需要判断新增的记录只能属于仓库1或2?

3.是否需要范围过滤

是否存在范围查询,比如只能看最近三天的数据