SpringBoot 數(shù)據(jù)權限新姿勢,注解+動態(tài)SQL真香!
介紹
easy-data-scop 是一個通過動態(tài)注入SQL實現(xiàn)的數(shù)據(jù)權限項目。支持MyBatis、MyBatis-plus、MyBatis-flex。使用簡單,無需設置各種復雜配置,僅僅通過注解便可實現(xiàn)效果功能。
基礎項目搭建
1.數(shù)據(jù)庫
圖片
這是一張簡單的用戶表,接下來我們將為這張表編寫以下數(shù)據(jù)權限:
- 僅看id為1的人
 - 僅看年齡為111的人
 - 僅看年齡為222的人
 - 看年齡為111、222的人
 
2.導入依賴基礎依賴 (使用MyBatis-plus、MyBatis 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.核心依賴
<dependency>
    <groupId>cn.zlinchuan</groupId>
    <artifactId>ds-mybatis</artifactId>
    <version>1.0.1</version>
</dependency>4.啟動類
@SpringBootApplication
publicclassMain{
    publicstaticvoidmain(String[] args){
        SpringApplication.run(Main.class);
    }
}5.省略編寫Mapper、Service
6.application.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.StdOutImpl7.測試
@Autowired
private UserService userService;
@Test
publicvoidtest(){
    
    userService.getAll().forEach(System.out::println);
}
圖片
到這里項目就已經搭建完成了。
使用 easy-data-scope
圖片
實現(xiàn)核心接口DataScopeFindRule 并交由Spring管理。
圖片
easy-data-scope 會去代理 @DataScope 方法調用 find() 獲取到 DataScopeInfo。
DataScopeInfo介紹
easy-data-scope 會根據(jù) find() 方法返回的 DataScopeInfo 列表來構建SQL。
圖片
@DataScope介紹
可以編寫在對應需要數(shù)據(jù)權限攔截的方法上。
屬性:
public@interface DataScope {
    /**
     * 通過傳遞給DataScopeFindRule.find方法來獲取指定的數(shù)據(jù)權限實體
     * @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 "";
    /**
     * 是否對數(shù)據(jù)權限進行自動合并
     * 當操作符為 =、!= 時間如果TableName、ColumnName、操作符一樣,并且使用的是 Value 形式將會對數(shù)據(jù)權限進行合并為 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
     */
    booleanmerge()defaultfalse;
    /**
     * 邏輯符
     * 決定數(shù)據(jù)權限SQL拼接到當前執(zhí)行的SQL中用的使用的是 WHERE還是AND還是OR..
     * TODO 注意:在flag為true時此值將會失效
     * @return
     */
    String logical()default SqlConsts.AND;
    /**
     * 是否使用數(shù)據(jù)權限標記位標記位,true是 false否
     * @return
     */
    booleanflag()defaultfalse;
}
圖片
實現(xiàn)前文的數(shù)據(jù)權限
編寫DataScopeFindRule find 方法。
@Override
public List<DataScopeInfo> find(String[] key){
    // 模擬的用戶登陸Session
    UserSessionInfo userSession = UserSessionContext.getUserSession();
    if (userSession != null) {
        // 數(shù)據(jù)庫中查詢
        QueryWrapper<AuthDatascopeEntity> idQueryWrapper = new QueryWrapper<>();
        // 查詢用戶Session中保存用戶有哪些數(shù)據(jù)權限
        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();
}創(chuàng)建數(shù)據(jù)權限表
-- auto-generated definition
createtable auth_datascope
(
    id                 int auto_increment comment'編號'
        primary key ,
    datascope_key      varchar(200)  nullcomment'數(shù)據(jù)權限標識' ,
    datascope_name     varchar(200)  nullcomment'數(shù)據(jù)權限名稱' ,
    datascope_tb_name  varchar(500)  nullcomment'數(shù)據(jù)權限表別名' ,
    datascope_col_name varchar(500)  nullcomment'數(shù)據(jù)權限字段名' ,
    datascope_op_name  varchar(10)   nullcomment'數(shù)據(jù)權限操作符' ,
    datascope_sql      varchar(5000) nullcomment'數(shù)據(jù)權限sql' ,
    datascope_value    varchar(200)  nullcomment'數(shù)據(jù)權限值' ,
    datascope_sort     int           nullcomment'數(shù)據(jù)權限排序' ,
    datascope_des      varchar(500)  nullcomment'數(shù)據(jù)權限描述'
)
    comment'數(shù)據(jù)權限表';1.只看Id為1的記錄
圖片
將對應實體添加到庫中,實現(xiàn)動態(tài)配置。
編寫Service:
@DataScope(keys = "USER_LIST_ID", logical = SqlConsts.WHERE)
public List<UserEntity> getAll(){
    return userMapper.selectList(null);
}調用后得到結果:
SELECTid,username,age FROMuserWHERE ( user.id = 1)2.僅看年齡為111的人
圖片
@DataScope(keys = "USER_LIST_AGE111", logical = SqlConsts.WHERE)
public List<UserEntity> getAll2(){
    return userMapper.selectList(null);
}調用后得到結果:
SELECTid,username,age FROMuserWHERE ( user.age = 111)3.僅看年齡為222的人
圖片
@DataScope(keys = "USER_LIST_AGE222", logical = SqlConsts.WHERE)
public List<UserEntity> getAll3(){
    return userMapper.selectList(null);
}調用后得到結果:
SELECTid,username,age FROMuserWHERE ( user.age = 222)4.看年齡為111、222的人(merge屬性)
其他的不用動,使用注解中的 merge 屬性,在keys中將兩個前兩個key都加上。
@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, merge = true, logical = SqlConsts.WHERE)
public List<UserEntity> getAll4(){
    return userMapper.selectList(null);
}調用后得到結果:
SELECTid,username,age FROMuserWHERE ( user.age IN (111, 222))更多操作
@DataScope.flag
Mapper.xml
@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, merge = true, flag = true)
List<UserEntity> getAll5();<selectid="getAll5"resultType="cn.zlinchuan.entity.UserEntity">
    select * from (select * from user where {{_DATA_SCOPE_FLAG}}) t where 1 = 1
</select>注意 {{_DATA_SCOPE_FLAG}} 為程序定義占位,不能修改。
sql
select * from (select * fromuserwhere user.age IN (111, 222)) t where1 = 1@DataScope.template
@DataScope(keys = {"USER_LIST_AGE111", "USER_LIST_AGE222"}, flag = true, template = "{{USER_LIST_AGE111}} OR {{USER_LIST_AGE222}}")
List<UserEntity> getAll6();<selectid="getAll6"resultType="cn.zlinchuan.entity.UserEntity">
    select * from (select * from user where {{_DATA_SCOPE_FLAG}}) t where 1 = 1
</select>sql
select * from (select * fromuserwhere user.age = 111OR user.age = 222) t where1 = 1














 
 
 













 
 
 
 