SpringBoot 數(shù)據(jù)權(quán)限新姿勢,注解+動態(tài)SQL真香!
介紹
easy-data-scop 是一個通過動態(tài)注入SQL實現(xiàn)的數(shù)據(jù)權(quán)限項目。支持MyBatis、MyBatis-plus、MyBatis-flex。使用簡單,無需設(shè)置各種復(fù)雜配置,僅僅通過注解便可實現(xiàn)效果功能。
基礎(chǔ)項目搭建
1.數(shù)據(jù)庫
圖片
這是一張簡單的用戶表,接下來我們將為這張表編寫以下數(shù)據(jù)權(quán)限:
- 僅看id為1的人
- 僅看年齡為111的人
- 僅看年齡為222的人
- 看年齡為111、222的人
2.導(dǎo)入依賴基礎(chǔ)依賴 (使用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.StdOutImpl
7.測試
@Autowired
private UserService userService;
@Test
publicvoidtest(){
userService.getAll().forEach(System.out::println);
}
圖片
到這里項目就已經(jīng)搭建完成了。
使用 easy-data-scope
圖片
實現(xiàn)核心接口DataScopeFindRule
并交由Spring管理。
圖片
easy-data-scope
會去代理 @DataScope
方法調(diào)用 find()
獲取到 DataScopeInfo
。
DataScopeInfo介紹
easy-data-scope
會根據(jù) find()
方法返回的 DataScopeInfo
列表來構(gòu)建SQL。
圖片
@DataScope介紹
可以編寫在對應(yīng)需要數(shù)據(jù)權(quán)限攔截的方法上。
屬性:
public@interface DataScope {
/**
* 通過傳遞給DataScopeFindRule.find方法來獲取指定的數(shù)據(jù)權(quán)限實體
* @return
*/
String[] keys();
/**
* 構(gòu)建模板
* TODO 注意:當(dāng)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ù)權(quán)限進(jìn)行自動合并
* 當(dāng)操作符為 =、!= 時間如果TableName、ColumnName、操作符一樣,并且使用的是 Value 形式將會對數(shù)據(jù)權(quán)限進(jìn)行合并為 IN、NOT IN
* 示例:
* 權(quán)限1:=、table1、column1、Value1 >>> table1.column1 = Value1
* 權(quán)限2:=、table1、column1、Value2 >>> table1.column1 = Value2
* 最終合并 in table1、column1、“Value1, Value2" >>> table1.column1 in (Value1, Value2)
* @return
*/
booleanmerge()defaultfalse;
/**
* 邏輯符
* 決定數(shù)據(jù)權(quán)限SQL拼接到當(dāng)前執(zhí)行的SQL中用的使用的是 WHERE還是AND還是OR..
* TODO 注意:在flag為true時此值將會失效
* @return
*/
String logical()default SqlConsts.AND;
/**
* 是否使用數(shù)據(jù)權(quán)限標(biāo)記位標(biāo)記位,true是 false否
* @return
*/
booleanflag()defaultfalse;
}
圖片
實現(xiàn)前文的數(shù)據(jù)權(quán)限
編寫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ù)權(quán)限
idQueryWrapper.in("id", userSession.getDataScopeIds());
idQueryWrapper.in("datascope_key", key);
List<AuthDatascopeEntity> authDatascopes = authDataSocpeMapper.selectList(idQueryWrapper);
// 構(gòu)建出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ù)權(quán)限表
-- auto-generated definition
createtable auth_datascope
(
id int auto_increment comment'編號'
primary key ,
datascope_key varchar(200) nullcomment'數(shù)據(jù)權(quán)限標(biāo)識' ,
datascope_name varchar(200) nullcomment'數(shù)據(jù)權(quán)限名稱' ,
datascope_tb_name varchar(500) nullcomment'數(shù)據(jù)權(quán)限表別名' ,
datascope_col_name varchar(500) nullcomment'數(shù)據(jù)權(quán)限字段名' ,
datascope_op_name varchar(10) nullcomment'數(shù)據(jù)權(quán)限操作符' ,
datascope_sql varchar(5000) nullcomment'數(shù)據(jù)權(quán)限sql' ,
datascope_value varchar(200) nullcomment'數(shù)據(jù)權(quán)限值' ,
datascope_sort int nullcomment'數(shù)據(jù)權(quán)限排序' ,
datascope_des varchar(500) nullcomment'數(shù)據(jù)權(quán)限描述'
)
comment'數(shù)據(jù)權(quán)限表';
1.只看Id為1的記錄
圖片
將對應(yīng)實體添加到庫中,實現(xiàn)動態(tài)配置。
編寫Service:
@DataScope(keys = "USER_LIST_ID", logical = SqlConsts.WHERE)
public List<UserEntity> getAll(){
return userMapper.selectList(null);
}
調(diào)用后得到結(jié)果:
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);
}
調(diào)用后得到結(jié)果:
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);
}
調(diào)用后得到結(jié)果:
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);
}
調(diào)用后得到結(jié)果:
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