1.背景
对于仓库过程流程,因为存在职能不同的多工种,多岗位,对于数据权限的要求也不相同。因此急需一套数据权限系统来保证业务流程正确性和数据安全性。
2.设计思路
数据权限系统,归根结底,是在对数据的CRUD操作时,对指定数据库中的指定表指定字段通过sql拦截的方式添加过滤条件。
在添加过滤条件之前,首先需要理解4个数据根源问题。
- 权限组(操作人对应的操作权限)
- 服务(权限组对应哪些服务)
- 表权限(服务中操作权限对应的数据权限)
- 字段权限(服务中数据权限对应的操作表字段)
权限组/服务/表/字段关系
人/岗位/权限组
3.功能实现
功能依赖如下组件
- xy-redis-starter
- xy-zookeeper-starter
1.服务启动注册数据权限定义(表,字段,字段类型)
2.服务实现数据权限中心提供的接口(泛化调用)
3.数据中心通过泛化调用创建权限组信息
4.数据中心绑定岗位与权限组关联关系
5.数据中心绑定员工与岗位关系(一对多)
6.服务调用权限中心接口获取用户当前服务权限数据
7.框架自动对删,改,查操作进行sql条件改写

4.配置细节
1.配置数据权限元数据
@Data@TableName("business_line")@DataScope(tableId = "business_line", desc = "用户",dataFields = {@DataField(fieldCode = "id", fieldDesc = "业务线Id", fieldType = FileTypeEnum.Long),@DataField(fieldCode = "status", fieldDesc = "状态", fieldType = FileTypeEnum.Integer)})public class BusinessLine {}
目前支持String,Long,Integer类型的字段过滤
2.引入permission-api
<dependency><groupId>com.trendsi</groupId><artifactId>trendsi-permission-api</artifactId><version>${permission-api.version}</version></dependency>
3.实现泛化接口提供数据
@Slf4j@DubboService(version = "1.0.0", group = "demo")public class DataScopeApiImpl implements DataScopeApi {@Overridepublic DataScopeInfo getScopeInitData() {DataScopeInfo dataScopeInfo = new DataScopeInfo();dataScopeInfo.setSystemCode("demo");List<TableFieldData> tableScopeInfos = new ArrayList<>();TableFieldData tableScopeInfo1 = new TableFieldData("user", "用户信息", "dept_id", "部门ID", "1001", "产品部门");TableFieldData tableScopeInfo2 = new TableFieldData("user", "用户信息", "dept_id", "部门ID", "1002", "研发部门");tableScopeInfos.add(tableScopeInfo1);tableScopeInfos.add(tableScopeInfo2);dataScopeInfo.setTableScopeInfos(tableScopeInfos);}}
version:版本固定1.0.0
group:当前服务的spring.application.name
4.在数据权限中心配置服务信息

后台服务表示后端提供的微服务
前段页面表示web页面
5.配置权限组

6.权限组绑定数据权限

1标识当前服务的那些表配置了数据权限
2标识当前表的哪些字段配置了权限数据
3.标识当前字段的值
5.权限字段代码生成
@Testpublic void generatorDataScope() {List<TableFieldData> list = new ArrayList<>();// 要扫描的包String packageName = "com.trendsi.permission";Reflections f = new Reflections(packageName);// 获取扫描到的标记注解的集合Set<Class<?>> set = f.getTypesAnnotatedWith(DataScope.class);System.err.println("List<TableFieldData> tableScopeInfos = new ArrayList<>();");//InterceptorIgnorefor (Class<?> c : set) {// 循环获取标记的注解DataScope dataScope = c.getAnnotation(DataScope.class);String tableId = dataScope.tableId();String desc = dataScope.desc();DataField[] dataFields = dataScope.dataFields();for (DataField dataField : dataFields) {String fieldCode = dataField.fieldCode();String fieldDesc = dataField.fieldDesc();TableFieldData data = new TableFieldData();data.setTableCode(tableId);data.setTableDesc(desc);data.setFieldCode(fieldCode);data.setFieldDesc(fieldDesc);data.setFieldValue("");data.setFieldValueDesc("");list.add(data);}}if (CollectionUtils.isEmpty(list)) {return;}Set<String> fieldCodeSet = list.stream().map(TableFieldData::getFieldCode).collect(Collectors.toSet());Iterator<String> iterator = fieldCodeSet.iterator();while (iterator.hasNext()) {String fieldCode = iterator.next();String mark = MessageFormat.format("//获取{0}的值和描述列表", fieldCode);System.err.println(mark);List<TableFieldData> fieldDataList = list.stream().filter(s -> XyStringUtils.equals(s.getFieldCode(), fieldCode)).collect(Collectors.toList());System.err.println("for (Object data : dataList ) {");for (TableFieldData tableFieldData : fieldDataList) {String format = MessageFormat.format("TableFieldData {0} = new TableFieldData(\"{1}\", \"{2}\", \"{3}\", \"{4}\", \"obj.data\", \"obj.dataDesc\");",tableFieldData.getTableCode(),tableFieldData.getTableCode(),tableFieldData.getTableDesc(),tableFieldData.getFieldCode(),tableFieldData.getFieldDesc());System.err.println(format);System.err.println("tableScopeInfos.add(" + tableFieldData.getTableCode() + ");");}System.err.println("}");}}
6.遗留问题
1.本次缓存同步
用户的权限数据存在本地缓存中,默认有效10分钟
若用户权限发生变更,如何实时更新用户的权限(本地缓存同步问题)
思路1:zk监听
思路2:mq舰艇
思路3:redis的发布订阅
2.新增是否过滤
例如:存在1,2,3,4四个仓库,员工A只有仓库1,2的数据权限,那么在A新增数据时,是否需要判断新增的记录只能属于仓库1或2?
3.是否需要范围过滤
是否存在范围查询,比如只能看最近三天的数据
