MyBatis-Plus攔截器實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制的方法
一、介紹
上篇文章介紹的MyBatis Plus 插件實(shí)際上就是用攔截器實(shí)現(xiàn)的,MyBatis Plus攔截器對MyBatis的攔截器進(jìn)行了包裝處理,操作起來更加方便
二、自定義攔截器
2.1、InnerInterceptor
MyBatis Plus提供的InnerInterceptor接口提供了如下方法,主要包括:在查詢之前執(zhí)行,在更新之前執(zhí)行,在SQL準(zhǔn)備之前執(zhí)行

2.2、編寫簡易攔截器
package com.xx.config;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.springframework.stereotype.Component;
import java.sql.Connection;
/**
* @author aqi
* @date 2023/5/17 15:07
*/
@Slf4j
@Component
public class TestInterceptor extends JsqlParserSupport implements InnerInterceptor {
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
// 這里固定這么寫就可以了
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
return;
}
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), null));
}
/**
* 該方法由JsqlParserSupport提供,主要用于通過API的方式操作SQL
* 思路:通過API構(gòu)建出新的條件,并將新的條件和之前的條件拼接在一起
*/
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
// 解析SQL
SelectBody selectBody = select.getSelectBody();
PlainSelect plainSelect = (PlainSelect) selectBody;
// 構(gòu)建eq對象
EqualsTo equalsTo = new EqualsTo(new Column("name"), new StringValue("tom"));
// 將原來的條件和新構(gòu)建的條件合在一起
AndExpression andExpression = new AndExpression(plainSelect.getWhere(), equalsTo);
// 重新封裝where條件
plainSelect.setWhere(andExpression);
}
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
insert.getColumns().add(new Column("name"));
((ExpressionList) insert.getItemsList()).getExpressions().add(new StringValue("tom"));
}
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
update.addUpdateSet(new Column("name"), new StringValue("tom"));
}
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
// 刪除新增條件和查詢一樣,不做演示
}
}2.3、將攔截器添加到MyBatis Plus攔截器中
package com.xx.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author aqi
* @date 2023/5/15 14:05
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 初始化Mybatis Plus攔截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TestInterceptor());
return interceptor;
}
}2.4、編寫測試用例
@Test
void save() {
AirlinesInfo airlinesInfo = new AirlinesInfo();
airlinesInfo.setInfo("remark");
airlinesInfoService.save(airlinesInfo);
}
@Test
void update() {
AirlinesInfo airlinesInfo = new AirlinesInfo();
airlinesInfo.setId(1L);
airlinesInfo.setInfo("remark, remark");
airlinesInfoService.updateById(airlinesInfo);
}
@Test
void select() {
airlinesInfoService.list();
}
2.5、執(zhí)行結(jié)果



三、自定義攔截器實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制
3.1、編寫攔截器
package com.xx.config;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.xx.entity.Permission;
import com.xx.utils.ExpressionUtils;
import com.xx.utils.UserUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.stereotype.Component;
import java.sql.Connection;
/**
* @author xiaxing
*/
@Slf4j
@Component
public class DataScopeInterceptor extends JsqlParserSupport implements InnerInterceptor {
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
log.info("[DataScopeInterceptor]beforePrepare...");
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.SELECT) {
if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
return;
}
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), null));
}
}
/**
* 查詢
*/
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
SelectBody selectBody = select.getSelectBody();
PlainSelect plainSelect = (PlainSelect) selectBody;
// 獲取表名/別名(如果是關(guān)聯(lián)查詢是取第一個(gè)join左側(cè)的表名/別名)
String tableName = ExpressionUtils.getTableName(plainSelect);
// 構(gòu)建用戶權(quán)限控制條件
Expression userPermissionExpression = this.buildUserPermissionSql(tableName);
if (null != userPermissionExpression) {
// 將sql原本就有得where條件和新構(gòu)建出來的條件拼接起來
plainSelect.setWhere(ExpressionUtils.appendExpression(plainSelect.getWhere(), userPermissionExpression));
}
}
/**
* 構(gòu)建用戶權(quán)限控制條件
* @param tableName 表名/別名(join查詢左側(cè)表名)
*/
private Expression buildUserPermissionSql(String tableName) {
// 獲取當(dāng)前用戶信息(這里的數(shù)據(jù)都是模擬的,實(shí)際上可能得從緩存或者session中獲取)
Permission permission = UserUtils.getUserPermission();
return null != permission ? ExpressionUtils.buildInSql(tableName + "." + permission.getField(), permission.getValue()) : null;
}
}3.2、編寫構(gòu)建SQL工具類
package com.xx.utils;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author aqi
* @date 2023/5/17 10:16
* @describe JSqlParser工具類,用于通過API的方式操作SQL語句
*/
public class ExpressionUtils {
/**
* 構(gòu)建in sql
* @param columnName 字段名稱
* @param params 字段值
* @return InExpression
*/
public static InExpression buildInSql(String columnName, Set<String> params) {
// 把集合轉(zhuǎn)變?yōu)镴SQLParser需要的元素列表
ItemsList itemsList = new ExpressionList(params.stream().map(StringValue::new).collect(Collectors.toList()));
// 創(chuàng)建IN表達(dá)式對象,傳入列名及IN范圍列表
return new InExpression(new Column(columnName), itemsList);
}
/**
* 構(gòu)建eq sql
* @param columnName 字段名稱
* @param value 字段值
* @return EqualsTo
*/
public static EqualsTo buildEq(String columnName, String value) {
return new EqualsTo(new Column(columnName), new StringValue(value));
}
/**
* 獲取表名/別名
* @param plainSelect plainSelect
* @return 表名/別名
*/
public static String getTableName(PlainSelect plainSelect) {
// 獲取別名
Table table= (Table) plainSelect.getFromItem();
Alias alias = table.getAlias();
return null == alias ? table.getName() : alias.getName();
}
/**
* 將2個(gè)where條件拼接到一起
* @param where 條件
* @param appendExpression 待拼接條件
* @return Expression
*/
public static Expression appendExpression(Expression where, Expression appendExpression) {
return null == where ? appendExpression : new AndExpression(where, appendExpression);
}
}3.3、模擬用戶信息工具類
package com.xx.utils;
import com.xx.config.Globle;
import com.xx.entity.Permission;
import com.xx.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* @author aqi
* @date 2023/5/17 14:20
*/
@Slf4j
public class UserUtils {
public static User currentUser;
static {
// 構(gòu)建測試數(shù)據(jù)
List<Permission> permissionList = new ArrayList<>();
// demo/test接口權(quán)限
Permission permission = new Permission();
permission.setField("id");
permission.setUri("/demo/test");
Set<String> set = new HashSet<>();
set.add("1");
set.add("2");
set.add("3");
permission.setValue(set);
permissionList.add(permission);
// demo/test1接口權(quán)限
Permission permission1 = new Permission();
permission1.setField("id");
permission1.setUri("/demo/test1");
Set<String> set1 = new HashSet<>();
set1.add("4");
set1.add("5");
set1.add("6");
permission1.setValue(set1);
permissionList.add(permission1);
User user = new User();
user.setPermissionList(permissionList);
user.setTenantId("1");
currentUser = user;
}
public static Permission getUserPermission() {
User currentUser = Globle.currentUser;
String uri = UserUtils.getUri();
List<Permission> permissionList = currentUser.getPermissionList();
return permissionList.stream().filter(e -> Objects.equals(e.getUri(), uri)).findFirst().orElse(null);
}
/**
* 獲取本次請求的uri
* @return uri
*/
private static String getUri() {
// 獲取此次請求的uri
String uri = "";
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (null != requestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
uri = request.getRequestURI();
}
log.info("[DataScopeInterceptor]此次請求uri:{}", uri);
return uri;
}
}3.4、將攔截器添加到MyBatis Plus藍(lán)機(jī)器中
package com.xx.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author aqi
* @date 2023/5/15 14:05
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 初始化Mybatis Plus攔截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new DataScopeInterceptor());
return interceptor;
}
}3.5、測試
package com.xx.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.xx.entity.AirlinesInfo;
import com.xx.service.AirlinesInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author aqi
* @date 2023/5/18 11:01
*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {
@Resource
private AirlinesInfoService airlinesInfoService;
@GetMapping("/test")
public void test() {
log.info("進(jìn)入test接口,測試權(quán)限控制在基礎(chǔ)的sql語句是否能生效");
airlinesInfoService.list();
// 執(zhí)行結(jié)果:(SELECT * FROM airlines_info WHERE state = 0 AND airlines_info.id IN ('1', '2', '3'))
}
@GetMapping("/test1")
public void test1() {
log.info("進(jìn)入test1接口,測試權(quán)限控制在使用MyBatis Plus 的分頁插件之后能否生效");
Page<AirlinesInfo> page = new Page<>(1, 5);
airlinesInfoService.page(page, new QueryWrapper<AirlinesInfo>().eq("name", "tom"));
// 執(zhí)行結(jié)果:(SELECT * FROM airlines_info WHERE state = 0 AND (name = ?) AND airlines_info.id IN ('4', '5', '6') LIMIT ?)
}
@GetMapping("/test2")
public void test2() {
log.info("進(jìn)入test2接口,測試權(quán)限控制在使用PageHelper之后能否生效");
PageHelper.startPage(1, 5);
airlinesInfoService.list(new LambdaQueryWrapper<AirlinesInfo>().eq(AirlinesInfo::getName, "tom"));
// 執(zhí)行結(jié)果:(SELECT * FROM airlines_info WHERE state = 0 AND (name = ?) AND airlines_info.id IN ('7', '8', '9') LIMIT ?)
}
@GetMapping("/test3")
public void test3() {
log.info("進(jìn)入test3接口,測試權(quán)限控制在使用自定義復(fù)雜關(guān)聯(lián)查詢之后能否生效");
airlinesInfoService.innerSql();
// 原始SQL:(select * from airlines_info t1 INNER JOIN t_config on t1.id = t_config.id where t1.name = 'tom' and t_config.name = 'jack' limit 5)
// 執(zhí)行結(jié)果:(SELECT * FROM airlines_info t1 INNER JOIN t_config ON t1.id = t_config.id WHERE t1.name = 'tom' AND t_config.name = 'jack' AND t1.id IN ('11', '12', '10') LIMIT 5)
}
@GetMapping("/test4")
public void test4() {
log.info("進(jìn)入test4接口,測試該接口沒有設(shè)計(jì)權(quán)限限制是否可以不生效");
airlinesInfoService.list();
// 執(zhí)行結(jié)果:(SELECT * FROM airlines_info WHERE state = 0)
}
}四、結(jié)論
通過測試可以看出不論在什么情況下都可以正常的對權(quán)限進(jìn)行控制
注意:上面部分代碼使用的是MyBatis Plus 3.5.3版本,并且使用的JSqlParser部分API已經(jīng)不推薦使用,但是我沒有找到最新的API應(yīng)該怎么寫
到此這篇關(guān)于MyBatis-Plus攔截器實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制的文章就介紹到這了,更多相關(guān)MyBatis Plus攔截器數(shù)據(jù)權(quán)限控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MybatisPlus攔截器如何實(shí)現(xiàn)數(shù)據(jù)表分表
- mybatis-plus配置攔截器實(shí)現(xiàn)sql完整打印的代碼設(shè)計(jì)
- Mybatis-plus通過添加攔截器實(shí)現(xiàn)簡單數(shù)據(jù)權(quán)限
- MybatisPlusInterceptor實(shí)現(xiàn)sql攔截器超詳細(xì)教程
- MyBatis-Plus攔截器實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制的示例
- mybatis-plus 攔截器敏感字段加解密的實(shí)現(xiàn)
- MyBatis-Plus攔截器對敏感數(shù)據(jù)實(shí)現(xiàn)加密
- mybatis-plus攔截器、字段填充器、類型處理器、表名替換、SqlInjector(聯(lián)合主鍵處理)
- mybatisplus 的SQL攔截器實(shí)現(xiàn)關(guān)聯(lián)查詢功能
- Mybatis Plus 3.4.0分頁攔截器的用法小結(jié)
相關(guān)文章
SpringBoot內(nèi)置服務(wù)器(Tomcat/Jetty/Undertow)切換的操作指南
本文介紹了如何在SpringBoot中切換內(nèi)置服務(wù)器(Tomcat、Jetty和Undertow),分析了它們的核心差異和適用場景,通過修改pom.xml和application.yml文件,可以實(shí)現(xiàn)服務(wù)器的切換和自定義配置,最后給出了選型建議和注意事項(xiàng),需要的朋友可以參考下2026-03-03
SpringBoot運(yùn)用AOP來實(shí)現(xiàn)分布式鎖的示例代碼
本文主要介紹了通過注解和AOP實(shí)現(xiàn)分布式鎖的方案,包含鎖過期時(shí)間、等待超時(shí)設(shè)置及自動續(xù)約功能,利用定時(shí)任務(wù)監(jiān)控鎖狀態(tài)并延長有效期,感興趣的可以了解一下2025-09-09
詳解Java使用雙異步后如何保證數(shù)據(jù)一致性
這篇文章主要為大家詳細(xì)介紹了Java使用雙異步后如何保證數(shù)據(jù)一致性,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以了解下2024-01-01
詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié))
本篇文章主要介紹了詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié)),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-11-11
SpringMVC轉(zhuǎn)發(fā)與重定向參數(shù)傳遞的實(shí)現(xiàn)詳解
這篇文章主要介紹了SpringMVC轉(zhuǎn)發(fā)與重定向參數(shù)傳遞,對于重定向,可以通過FlashMap或RedirectAttributes來在請求間傳遞數(shù)據(jù),因?yàn)橹囟ㄏ蛏婕皟蓚€(gè)獨(dú)立的HTTP請求,而轉(zhuǎn)發(fā)則在同一請求內(nèi)進(jìn)行,數(shù)據(jù)可以直接通過HttpServletRequest共享,需要的朋友可以參考下2022-07-07
mybatis plus代碼生成工具的實(shí)現(xiàn)代碼
這篇文章主要介紹了mybatis plus代碼生成工具的實(shí)現(xiàn)代碼,需要的朋友可以參考下2021-04-04

