数据权限框架(easy-data-scope)

介绍

easy-data-scop 是一个通过动态注入SQL实现的数据权限项目。支持MyBatis、MyBatis-plus、MyBatis-flex。使用简单,无需设置各种复杂配置,仅仅通过注解便可实现效果功能。

基础项目搭建

1.数据库

这是一张简单的用户表,接下来我们将为这张表编写以下数据权限

  1. 仅看id为1的人
  2. 仅看年龄为111的人
  3. 仅看年龄为222的人
  4. 看年龄为111、222的人

2.导入依赖基础依赖 (使用MyBatis-plus、MyBatis XML演示)

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>2.2.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

3.核心依赖

xml 复制代码
<dependency>
    <groupId>cn.zlinchuan</groupId>
    <artifactId>ds-mybatis</artifactId>
    <version>1.0.1</version>
</dependency>

4.启动类

java 复制代码
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class);
    }
}

6.application.yml

yml 复制代码
server:
  port: 8001
# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: url
    username: name
    password: password
mybatis:
  mapper-locations: classpath:mapper/*.xml  # XML映射文件路径
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5.省略编写Mapper、Service

6.测试

java 复制代码
@Autowired
private UserService userService;

@Test
public void test() {
    
    userService.getAll().forEach(System.out::println);
}

到这里项目就已经搭建完成了

使用 easy-data-scope

实现核心接口DataScopeFindRule 并交由Spring管理

easy-data-scope 会去代理 @DataScope 方法调用 find() 获取到 DataScopeInfo

DataScopeInfo介绍

easy-data-scope 会根据 find() 方法返回的 DataScopeInfo 列表来构建SQL

@DataScope介绍

可以编写在对应需要数据权限拦截的方法上

属性:

java 复制代码
public @interface DataScope {
    /**
     * 通过传递给DataScopeFindRule.find方法来获取指定的数据权限实体
     * @return
     */
    String[] keys();

    /**
     * 构建模板
     * TODO 注意:当key为多个时此值生效
     * key1 ==SQL==> table1.column1 = 1
     * key2 ==SQL==> table2.column2 = 2
     * 示例:template = "{key1} OR {key2}"
     * 通过template生成后的SQL:table1.column1 = 1 OR table2.column2 = 2
     * @return
     */
    String template() default "";

    /**
     * 是否对数据权限进行自动合并
     * 当操作符为 =、!= 时间如果TableName、ColumnName、操作符一样,并且使用的是 Value 形式将会对数据权限进行合并为 IN、NOT IN
     * 示例:
     * 权限1:=、table1、column1、Value1 >>> table1.column1 = Value1
     * 权限2:=、table1、column1、Value2 >>> table1.column1 = Value2
     * 最终合并 in table1、column1、"Value1, Value2" >>> table1.column1 in (Value1, Value2)
     * @return
     */
    boolean merge() default false;

    /**
     * 逻辑符
     * 决定数据权限SQL拼接到当前执行的SQL中用的使用的是 WHERE还是AND还是OR..
     * TODO 注意:在flag为true时此值将会失效
     * @return
     */
    String logical() default SqlConsts.AND;

    /**
     * 是否使用数据权限标记位标记位,true是 false否
     * @return
     */
    boolean flag() default false;
}

实现前文的数据权限

编写DataScopeFindRule find 方法

java 复制代码
@Override
public List<DataScopeInfo> find(String[] key) {
    // 模拟的用户登陆Session
    UserSessionInfo userSession = UserSessionContext.getUserSession();
    if (userSession != null) {
        // 数据库中查询
        QueryWrapper<AuthDatascopeEntity> idQueryWrapper = new QueryWrapper<>();
        // 查询用户Session中保存用户有哪些数据权限
        idQueryWrapper.in("id", userSession.getDataScopeIds());
        idQueryWrapper.in("datascope_key", key);
        List<AuthDatascopeEntity> authDatascopes = authDataSocpeMapper.selectList(idQueryWrapper);
        // 构建出DataScopeInfo
        List<DataScopeInfo> dataScopeInfos = new ArrayList<>(authDatascopes.size());
        for (AuthDatascopeEntity authDatascope : authDatascopes) {
            DataScopeInfo dataScopeInfo = new DataScopeInfo();
            dataScopeInfo.setKey(authDatascope.getDatascopeKey());
            dataScopeInfo.setOperator(authDatascope.getDatascopeOpName());
            dataScopeInfo.setTableName(authDatascope.getDatascopeTbName());
            dataScopeInfo.setColumnName(authDatascope.getDatascopeColName());
            dataScopeInfo.setSql(authDatascope.getDatascopeSql());
            dataScopeInfo.setValue(authDatascope.getDatascopeValue());
            dataScopeInfo.setSort(authDatascope.getDatascopeSort());
            dataScopeInfos.add(dataScopeInfo);
        }
        return dataScopeInfos;
    }

    return Collections.emptyList();
}

创建数据权限表

sql 复制代码
-- auto-generated definition
create table auth_datascope
(
    id                 int auto_increment comment '编号'
        primary key ,
    datascope_key      varchar(200)  null comment '数据权限标识' ,
    datascope_name     varchar(200)  null comment '数据权限名称' ,
    datascope_tb_name  varchar(500)  null comment '数据权限表别名' ,
    datascope_col_name varchar(500)  null comment '数据权限字段名' ,
    datascope_op_name  varchar(10)   null comment '数据权限操作符' ,
    datascope_sql      varchar(5000) null comment '数据权限sql' ,
    datascope_value    varchar(200)  null comment '数据权限值' ,
    datascope_sort     int           null comment '数据权限排序' ,
    datascope_des      varchar(500)  null comment '数据权限描述'
)
    comment '数据权限表';

1.只看Id为1的记录

将对应实体添加到库中,实现动态配置

编写Service

java 复制代码
@DataScope(keys = "USER_LIST_ID", logical = SqlConsts.WHERE)
public List<UserEntity> getAll() {
    return userMapper.selectList(null);
}

调用后得到结果

sql 复制代码
SELECT id,username,age FROM user WHERE ( user.id = 1)

2.仅看年龄为111的人

java 复制代码
@DataScope(keys = "USER_LIST_AGE111", logical = SqlConsts.WHERE)
public List<UserEntity> getAll2() {
    return userMapper.selectList(null);
}

sql

sql 复制代码
SELECT id,username,age FROM user WHERE ( user.age = 111)

2.仅看年龄为222的人

java 复制代码
@DataScope(keys = "USER_LIST_AGE222", logical = SqlConsts.WHERE)
public List<UserEntity> getAll3() {
    return userMapper.selectList(null);
}

sql

sql 复制代码
SELECT id,username,age FROM user WHERE ( user.age = 222)

3.看年龄为111、222的人(merge属性)

其他的不用动,使用注解中的 merge 属性,在keys中将两个前两个key都加上

java 复制代码
@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, merge = true, logical = SqlConsts.WHERE)
public List<UserEntity> getAll4() {
    return userMapper.selectList(null);
}

sql

sql 复制代码
SELECT id,username,age FROM user WHERE ( user.age IN (111, 222))

更多操作

@DataScope.flag

Mapper.xml

java 复制代码
@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, merge = true, flag = true)
List<UserEntity> getAll5();
xml 复制代码
<select id="getAll5" resultType="cn.zlinchuan.entity.UserEntity">
    select * from (select * from user where {{_DATA_SCOPE_FLAG}}) t where 1 = 1
</select>

注意 {{_DATA_SCOPE_FLAG}} 为程序定义占位,不能修改

sql

sql 复制代码
select * from (select * from user where user.age IN (111, 222)) t where 1 = 1

@DataScope.template

java 复制代码
@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, flag = true, template = "{{USER_LIST_AGE111}} OR {{USER_LIST_AGE222}}")
List<UserEntity> getAll6();
xml 复制代码
<select id="getAll6" resultType="cn.zlinchuan.entity.UserEntity">
    select * from (select * from user where {{_DATA_SCOPE_FLAG}}) t where 1 = 1
</select>

sql

sql 复制代码
select * from (select * from user where user.age = 111 OR user.age = 222) t where 1 = 1

演示项目地址

gitee.com/zoulinchuan...

项目源码地址

github.com/zoulinchuan...

相关推荐
老前端的功夫22 分钟前
TypeScript 类型魔术:模板字面量类型的深层解密与工程实践
前端·javascript·ubuntu·架构·typescript·前端框架
uzong22 分钟前
软件架构指南 Software Architecture Guide
后端
又是忙碌的一天22 分钟前
SpringBoot 创建及登录、拦截器
java·spring boot·后端
min1811234561 小时前
PC端零基础跨职能流程图制作教程
大数据·人工智能·信息可视化·架构·流程图
静听松涛1331 小时前
中文PC端多人协作泳道图制作平台
大数据·论文阅读·人工智能·搜索引擎·架构·流程图·软件工程
勇哥java实战分享1 小时前
短信平台 Pro 版本 ,比开源版本更强大
后端
学历真的很重要1 小时前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
计算机毕设VX:Fegn08951 小时前
计算机毕业设计|基于springboot + vue二手家电管理系统(源码+数据库+文档)
vue.js·spring boot·后端·课程设计
上进小菜猪2 小时前
基于 YOLOv8 的智能杂草检测识别实战 [目标检测完整源码]
后端
韩师傅3 小时前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端