mybatis实现数据行级权限拦截
最近在做一个测试平台,其中有一个需求是用户只能看到他有权限的项目数据。一开始这个需求只针对用例模块,我直接在sql后面加上了关联项目权限表。后面因为其他模块也需要这个权限判断,故打算把关联sql抽取出来,实现拦截sql并添加权限。
大概分为三步:
1、定义一个注解 PermissionAop (可以在注解中添加参数,实现不同参数做不同的sql拼接)
2、实现拦截器(参考pagehelper的PageInterceptor实现),拦截Executor,当遇到mapper方法带有注解A,把权限的sql拼接上去
3、添加拦截器,可以使用@Configuration或实现ApplicationListener(自行百度),先添加pagehelper拦截器,再添加自定义的数据权限拦截器
下面说下要注意的点:
1、注意pom文件引入的是pagehelper-spring-boot-starter还是pagehelper。引入pagehelper-spring-boot-starter会自动添加pagehelper拦截器,而引入pagehelper则要手动添加拦截器。(我使用的pagehelper,下面伪代码有。若使用pagehelper-spring-boot-starter,参考https://blog.csdn.net/weixin_45497155/article/details/106025321)
2、测试时发现生成的sql总是先limit再权限过滤,pagehelper的拦截一直优先于自定义的拦截(这样会出现limit出10条,权限过滤剩5条,实际要求是先权限过滤,再limit出10条),一开始以为是添加拦截器的顺序问题,后面在官方文档看到,是拦截的对象不对(百度很多文章写的都是拦截StatementHandler),应该要拦截Executor
详细参考pagehelper官方文档:https://gitee.com/free/Mybatis_PageHelper/blob/master/wikis/zh/Interceptor.md
伪代码:
注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @Author: Hjy
* @Date: 2021/8/18 15:38 (日期和时间)
* @description 自定义权限Aop注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionAop {
PermissionEnum type() default PermissionEnum.DEFAULT;
}
拦截器:
/**
* @Author: Hjy
* @Date: 2021/11/4 15:01 (日期和时间)
* @description sql权限拦截(不能拦截StatementHandler , pageHelper拦截的是Executor
*具体参考 : https://gitee.com/free/Mybatis_PageHelper/blob/master/wikis/zh/Interceptor.md )
*/
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class SqlAuthenticationInterceptor implements Interceptor { @Override
public Object intercept(Invocation invocation) throws Throwable { // 这一段复制pageHelper的PageInterceptor
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
if (args.length == 4) {
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
} // 获取全类名和方法名
String id = ms.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
final Class<?> cls = Class.forName(className);
final Method[] methods = cls.getMethods();
for (Method method : methods) {
// 若该方法含有PermissionAop注解,添加权限sql
if (method.getName().equals(methodName) && method.isAnnotationPresent(PermissionAop.class)) {
String permissionSql = getPermissionSql(boundSql.getSql());
ReflectUtil.setFieldValue(boundSql, "sql", permissionSql);
}
}
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
} @Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
} @Override
public void setProperties(Properties properties) { } private String getPermissionSql(String boundSql) {
// 这里拼接你的权限sql
return boundSql+"";
}
}
配置拦截器
/**
* @Author: Hjy
* @Date: 2021/11/10 15:39 (日期和时间)
* @description 自定义mybatis的拦截,不使用pagehelper-spring-boot-starter,目的是添加拦截数据权限
*/
@Configuration
public class MybatisInterceptorAutoConfiguration { @Resource
private List<SqlSessionFactory> sqlSessionFactoryList; @Bean
@ConfigurationProperties(prefix = "pagehelper")
public Properties pageHelperProperties() {
return new Properties();
} @PostConstruct
public void addMysqlInterceptor() {
//数据权限拦截器
SqlAuthenticationInterceptor sqlAuthenticationInterceptor = new SqlAuthenticationInterceptor();
//分页拦截器
PageInterceptor pageInterceptor = new PageInterceptor();
pageInterceptor.setProperties(this.pageHelperProperties()); // 先增加的拦截后执行
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
sqlSessionFactory.getConfiguration().addInterceptor(pageInterceptor);
// 增加权限拦截
sqlSessionFactory.getConfiguration().addInterceptor(sqlAuthenticationInterceptor);
}
}
}
mybatis实现数据行级权限拦截的更多相关文章
- Quick BI独创千人千面的行级权限管控机制
摘要 就数据访问权限而言,阿里巴巴以“被动式授权”为主,你需要什么权限就申请什么权限.但是,在客户交流过程中,我们发现绝大多数企业都是集中式授权,尤其是面向个人的行级权限管控,管理复杂度往往呈几何增长 ...
- Security Policy:行级安全(Row-Level Security)
行级安全RLS(Row-Level Security)是在数据行级别上控制用户的访问,控制用户只能访问数据库表的特定数据行.断言是逻辑表达式,在SQL Server 2016中,RLS是基于安全断言( ...
- 行级安全(Row-Level Security)
通过授予和拒绝(Grant/Deny)命令控制用户的权限,只能控制用户对数据库对象的访问权限,这意味着,用户访问的粒度是对象整体,可以是一个数据表,或视图等,用户要么能够访问数据库对象,要么没有权限访 ...
- 行级安全(Row
通过授予和拒绝(Grant/Deny)命令控制用户的权限,只能控制用户对数据库对象的访问权限,这意味着,用户访问的粒度是对象整体,可以是一个数据表,或视图等,用户要么能够访问数据库对象,要么没有权限访 ...
- SSAS中CUBE行权限数据级权限控制
去年做了一个数据仓库的项目,其中涉及到了CUBE数据级权限的控制.在网上找这方面的资料,找到一个[BI] 通用数据级权限控制解决方案的实现(二):Cube中的角色设置与数据级权限控制.根据这个大牛的思 ...
- InnoDB这种行锁实现特点意味者:只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁!
InnoDB行锁是通过索引上的索引项来实现的,这一点MySQL与Oracle不同,后者是通过在数据中对相应数据行加锁来实现的. InnoDB这种行锁实现特点意味者:只有通过索引条件检索数据,InnoD ...
- MySQL行(记录)的详细操作一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理
MySQL行(记录)的详细操作 阅读目录 一 介绍 二 插入数据INSERT 三 更新数据UPDATE 四 删除数据DELETE 五 查询数据SELECT 六 权限管理 一 介绍 MySQL数据操作: ...
- mysql:如何解决数据修改冲突(事务+行级锁的实际运用)
摘要:最近做一个接诊需求遇到一个问题,假设一个订单咨询超过3次就不能再接诊,但如果两个医生同时对该订单进行咨询,查数据库的时候查到的接诊次数都是2次,那两个医生都能接诊,所谓接诊可以理解为更新了接诊次 ...
- sqlserver数据库安全函数、配置函数、游标函数、行级函数、排名函数、元数据函数、系统统计函数 、文本和图像函数--收藏着有用
行级函数:下列行集函数将返回一个可用于代替 Transact-SQL 语句中表引用的对象. CONTAINSTABLE 返回具有零行.一行或多行的表,这些行的列中包含的基于字符类型的数据是单个词语和短 ...
随机推荐
- 【Java】学习路径33-在IDEA中使用junit单元测试运行单个方法
首先在菜单找到 文件-项目结构 项目设置-库-"+" 选择Java 找到IDEA安装目录,打开lib/junit4.jar 的jar包,然后打开即可. 然后选择确定. 然后再项目中 ...
- Java数据类型扩展及面试题讲解
这是关于数据类型的面试题,还是比较重要的. 1.整数拓展: 我们肯定都或多或少知道一个问题,叫进制问题.咱们常见的电脑里的二进制,然后咱们平时生活中用的是十进制,然后咱们工作的时候还有一个十六进制跟八 ...
- 【读书笔记】C#高级编程 第三章 对象和类型
(一)类和结构 类和结构实际上都是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法. 类和结构的区别:内存中的存储方式.访问方式(类是存储在堆上的引用类型,结构是存储在栈的值类型)和它 ...
- Windows打印服务器上无法删除打印机
这几天遇到了一个问题,在Windows 2008的打印服务器上的打印机无法删除.具体表现是可以在设备和打印机里删除打印机,然后刷新一下,它们又出来了.这些打印机早就不存在了,并且这些打印机的图标呈半透 ...
- Openstack neutron:目录
为什么? 最近一直在学习SDN方面的知识,本着"最好的学习就是分享"的精神,记录下本系列的文章,尝试更好地去理解SDN这一正当红的技术. 如何? SDN领域现在已经充斥了大量的公司 ...
- HCIA-STP原理与配置
STP协议生成树协议: 为了保证网络可靠,所以在组网时需要设置冗余链路和设备,从而在物理结构上形成结构,又因为交换机的工作特点导致二层网络中产生广播风暴和MAC地址表震荡现象,影响用户体验. 广播风暴 ...
- 【gRPC】C++异步服务端优化版,多服务接口样例
官方的C++异步服务端API样例可读性并不好,理解起来非常的费劲,各种状态机也并不明了,整个运行过程也容易读不懂,因此此处参考网上的博客进行了重写,以求顺利读懂. C++异步服务端实例,详细注释版 g ...
- Django 使用VScode 创建工程
一.VSCode 创建Django 工程 VSCode 官方: https://code.visualstudio.com 1 mysite(项目名),创建Django 项目,可以和虚拟环境放在同一目 ...
- Elasticsearch:Dynamic mapping
Elasticsearch最重要的功能之一是它试图摆脱你的方式,让你尽快开始探索你的数据. 要索引文档,您不必首先创建索引,定义映射类型和定义字段 - 您只需索引文档,那么index,type和fie ...
- Logstash:如何使用Elasticsearch,Logstash和Kibana管理Apache日志