wbase-data-permission
大约 4 分钟
wbase-data-permission
数据权限模块 - 基于 MyBatis Plus 拦截器实现的通用数据权限控制
📦 Maven 依赖
<dependency>
<groupId>com.wzhcode.wbase</groupId>
<artifactId>wbase-data-permission</artifactId>
<version>1.0.0</version>
</dependency>📁 目录结构
wbase-data-permission/src/main/java/com/wzhcode/wbase/data/permission/
│
├── 📂 config/ # 自动配置
├── 📂 core/
│ ├── 📂 annotation/ # 数据权限注解
│ ├── 📂 aop/ # AOP拦截器
│ ├── 📂 db/ # 数据库拦截器(SQL重写)
│ ├── 📂 rule/ # 数据权限规则接口
│ └── 📂 util/ # 工具类📑 目录
🎯 核心原理
通过 MyBatis Plus 拦截器,在 SQL 执行前自动追加 WHERE 条件,实现数据权限过滤。
┌─────────────────────────────────────────────────────────────┐
│ 执行流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. 方法调用 → @DataPermission 注解入栈 │
│ 2. SQL 执行 → DataPermissionDatabaseInterceptor 拦截 │
│ 3. 规则匹配 → DataPermissionRuleFactory 获取规则 │
│ 4. SQL 重写 → 追加 WHERE 条件 │
│ 5. 方法返回 → @DataPermission 注解出栈 │
└─────────────────────────────────────────────────────────────┘SQL 重写示例
-- 原始 SQL
SELECT * FROM user WHERE status = 1
-- 重写后(假设规则为部门数据权限)
SELECT * FROM user WHERE status = 1 AND dept_id IN (1, 2, 3)📋 规则接口
位置:
com.wzhcode.wbase.data.permission.core.rule
接口层级
BaseDataPermissionRule # 基础接口
├── ReadWriteDataPermissionRule # 读写分离规则
│ └── DataPermissionRule # 统一读写规则BaseDataPermissionRule
最基础的规则接口,支持对 SELECT/INSERT/UPDATE/DELETE 分别定义权限:
public interface BaseDataPermissionRule {
// 返回需要生效的表名
Set<String> getTableNames();
// 查询时的过滤条件
Expression getSelectExpression(String tableName, Alias tableAlias);
// 插入时的过滤条件
Expression getInsertExpression(String tableName, Alias tableAlias);
// 更新时的过滤条件
Expression getUpdateExpression(String tableName, Alias tableAlias);
// 删除时的过滤条件
Expression getDeleteExpression(String tableName, Alias tableAlias);
// 是否默认启用
default boolean isEnable() { return true; }
}ReadWriteDataPermissionRule
读写分离规则,将操作分为读(SELECT)和写(INSERT/UPDATE/DELETE):
public interface ReadWriteDataPermissionRule extends BaseDataPermissionRule {
// 读数据权限
Expression getReadExpression(String tableName, Alias tableAlias);
// 写数据权限
Expression getWriteExpression(String tableName, Alias tableAlias);
}DataPermissionRule
统一规则,读写使用相同的权限条件:
public interface DataPermissionRule extends ReadWriteDataPermissionRule {
// 统一的权限表达式
Expression getExpression(String tableName, Alias tableAlias);
}📝 自定义规则示例
@Component
public class DeptDataPermissionRule implements DataPermissionRule {
@Override
public Set<String> getTableNames() {
return Set.of("sys_user", "sys_order");
}
@Override
public Expression getExpression(String tableName, Alias tableAlias) {
// 获取当前用户可访问的部门ID
Set<Long> deptIds = SecurityUtils.getLoginUserDeptIds();
if (CollUtil.isEmpty(deptIds)) {
return null; // 无权限限制
}
// 构建 dept_id IN (1, 2, 3) 条件
String column = tableAlias != null
? tableAlias.getName() + ".dept_id"
: "dept_id";
return new InExpression(new Column(column),
new ExpressionList(deptIds.stream()
.map(LongValue::new)
.collect(Collectors.toList())));
}
}🏷️ 注解使用
位置:
com.wzhcode.wbase.data.permission.core.annotation
@DataPermission 注解
可声明在类或方法上,控制数据权限的启用和规则选择:
| 属性 | 类型 | 说明 |
|---|---|---|
enable | boolean | 是否启用数据权限,默认 true |
anyRules | Class[] | 包含默认规则 + 指定规则 |
includeRules | Class[] | 仅使用指定规则(优先级高) |
excludeRules | Class[] | 排除指定规则(优先级低) |
📝 使用示例
// ========== 禁用数据权限 ==========
@DataPermission(enable = false)
public List<User> listAllUsers() {
return userMapper.selectList(null);
}
// ========== 仅使用部门规则 ==========
@DataPermission(includeRules = DeptDataPermissionRule.class)
public List<User> listDeptUsers() {
return userMapper.selectList(null);
}
// ========== 排除某个规则 ==========
@DataPermission(excludeRules = TenantDataPermissionRule.class)
public List<User> listUsersWithoutTenant() {
return userMapper.selectList(null);
}
// ========== 类级别声明 ==========
@DataPermission(enable = false)
@Service
public class SystemConfigService {
// 该类所有方法都不启用数据权限
}🔧 工具类
位置:
com.wzhcode.wbase.data.permission.core.util
DataPermissionUtils
编程式控制数据权限:
// 忽略数据权限执行(Runnable)
DataPermissionUtils.executeIgnore(() -> {
// 这里的查询不会追加数据权限条件
userMapper.selectList(null);
});
// 忽略数据权限执行(Callable,有返回值)
List<User> users = DataPermissionUtils.executeIgnore(() -> {
return userMapper.selectList(null);
});DataPermissionContextHolder
数据权限上下文,支持嵌套调用:
// 获取当前数据权限配置
DataPermission current = DataPermissionContextHolder.get();
// 手动入栈/出栈(一般不需要手动调用)
DataPermissionContextHolder.add(dataPermission);
DataPermissionContextHolder.remove();
// 获取所有(嵌套场景)
List<DataPermission> all = DataPermissionContextHolder.getAll();⚙️ 自动配置
位置:
com.wzhcode.wbase.data.permission.config
自动注册的 Bean
| Bean | 说明 |
|---|---|
DataPermissionRuleFactory | 规则工厂,管理所有 BaseDataPermissionRule |
DataPermissionDatabaseInterceptor | 数据库拦截器,自动注册到 MybatisPlusInterceptor |
DataPermissionAnnotationAdvisor | AOP 切面,处理 @DataPermission 注解 |
配置说明
- 自动扫描所有实现
BaseDataPermissionRule的 Bean - 拦截器自动添加到 MybatisPlusInterceptor 的首位(在分页插件之前)
- 支持
TransmittableThreadLocal,线程池场景下上下文正确传递
📚 主要依赖
| 依赖 | 用途 | 版本管理 |
|---|---|---|
| wbase-mybatis | MyBatis Plus 基础 | ✅ |
| transmittable-thread-local | 线程上下文传递 | ✅ |
| JSqlParser | SQL 解析与重写 | ✅ |
版本由
wbase-dependencies统一管理
💡 最佳实践
| 场景 | 推荐做法 |
|---|---|
| 🔧 规则定义 | 实现 DataPermissionRule 接口,注册为 Spring Bean |
| 🏷️ 临时禁用 | 使用 @DataPermission(enable = false) 或 DataPermissionUtils.executeIgnore() |
| 📊 读写分离 | 实现 ReadWriteDataPermissionRule,分别定义读写权限 |
| 🔄 规则选择 | 使用 includeRules 精确指定,避免使用 excludeRules |
| 🧵 异步场景 | 已集成 TTL,线程池场景自动传递上下文 |
| ⚡ 性能优化 | 拦截器内置缓存,无需重写的 SQL 会被缓存跳过 |