07-数据权限 common-permission¶
📄 创建: ZCode AI 2026-08-14 · 修改: JimWb 2026-08-14
common-permission(13 个类)基于 MyBatis 拦截器实现行级数据权限:在 SQL 执行前动态拼接 WHERE 条件,按公司/部门/本人等维度自动过滤数据。
一、开启数据权限¶
启动类加注解¶
@EnableDataPermission(cn.cnbm.bams.permission.startup.EnableDataPermission)通过 @ImportAutoConfiguration 导入三个 Bean。
配置项¶
project:
check-permission: true # 是否启用数据权限(默认 true)
dynamic:
datasource:
db-type: mysql # mysql 或 dm(达梦)
exclude-tables: sys_user,sys_role # 排除的表(逗号分隔,不过滤这些表)
spring:
check-session:
enabled: true # 是否校验 session
二、6 级数据范围¶
cn.cnbm.bams.common.constant.PermissionConstants
数据权限范围由角色的 dataScope 字段决定:
| 常量 | 值 | 含义 | 拼接条件(表有对应列时) |
|---|---|---|---|
DATA_ALL |
1 | 全部数据 | 无条件(放行) |
DATA_CORP_AND_CHILDREN |
2 | 本公司及子公司 | corp_code IN (本司+所有子司) |
DATA_CORP |
3 | 本公司 | corp_code = '当前公司' |
DATA_DEPT |
4 | 本部门 | dept_code IN (deptList) |
DATA_DEPT_AND_CHILDREN |
5 | 本部门及子部门 | dept_code IN (deptList) |
DATA_SELF |
6 | 仅本人 | create_by = userId |
DATA_CORP_AND_PARENT |
999 | 本公司及上级 | corp_code IN (本司+所有父司) |
| 默认 | — | 未知范围 | 1 = 2(全部不可见) |
前提:业务表需有
corp_code/dept_code/create_by列才会被自动过滤。列名通过information_schema(MySQL)或ALL_TAB_COLUMNS(达梦)检测。
三、SQL 改写机制¶
工作流程¶
1. DataPermissionHandlerInterceptor (Spring 拦截器)
│ preHandle: 从 Session 取 corpCode/dataScope → 存入 PermissionContextHolder (ThreadLocal)
│ afterCompletion: clear() 清理 ThreadLocal
▼
2. DataScopeAspect (AOP 切面)
│ 切 cn.cnbm.*.controller..* 的方法
│ 检查 @IgnoreSession / @CommonData / @RequireDataScope 调整 dataScope
│ 校验 session 有效性
▼
3. DataPermissionInterceptor (MyBatis 拦截器) ← 核心
│ 拦截 Executor.query
│ 取 dataScope,超管/无 dataScope → 放行
│ @InterceptorIgnore(dataPermission="true") → 放行
│ 用 JSqlParser 解析 SELECT AST
│ 按表名+数据范围生成 WHERE 条件
│ AND 到原 WHERE
│ 反射修改 BoundSql 的 sql 字段
▼
4. 执行改写后的 SQL
``### DataPermissionInterceptor
`cn.cnbm.bams.permission.interceptors.ibatis.DataPermissionInterceptor`
`@Intercepts` 拦截 `Executor.query` 的两个重载。
关键点:
- 用 **CCJSqlParserManager**(JSqlParser)解析 SQL 为 Select AST
- 支持 PlainSelect 和 SetOperationList(UNION)
- 用 `ReflectUtil.setFieldValue(boundSql, "sql", newSql)` 反射修改原 BoundSql(不能 new BoundSql,否则 `#{}` 报错)
- 改写前后 SQL 均为 debug 级日志
### DefaultDataPermissionHandler
`cn.cnbm.bams.permission.interceptors.ibatis.handler.impl.DefaultDataPermissionHandler`
WHERE 条件生成器,按 `dataScope` 值 switch 生成对应条件(见上表)。
依赖:
- `JdbcTemplate`:查列是否存在
- `ICommonService`:查公司层级(子公司/父公司链)
---
## 四、控制注解
### @CommonData —— 查上级数据
`cn.cnbm.bams.common.annotations.permission.CommonData`
标记在 Controller 类/方法上,把 dataScope 设为 `DATA_CORP_AND_PARENT`(本公司及上级公司)。
```java
@RestController
@RequestMapping("/order")
@CommonData // 类级别:所有查询都能看到上级公司数据
public class OrderController extends EnergyBaseController<...> {
@PostMapping("/list")
@CommonData // 方法级别(覆盖类级别)
public Object list() { ... }
}
@RequireDataScope —— 关闭权限¶
@IgnoreSession —— 跳过 session 校验¶
@InterceptorIgnore —— Mapper 级跳过¶
MyBatis-Plus 原生注解,标记在 Mapper 方法/类上:
@Mapper
@InterceptorIgnore(dataPermission = "true") // 这个 Mapper 不加数据权限
public interface MyMapper extends BaseMapperExt<MyEntity> { }
五、权限上下文¶
PermissionContextHolder¶
cn.cnbm.bams.permission.ctx.PermissionContextHolder
ThreadLocal
| 方法 | 功能 |
|---|---|
getRequestContext() |
取上下文 |
clear() |
清除(防内存泄漏) |
PermissionContext¶
cn.cnbm.bams.common.constant.PermissionContext
封装 Dict ctx,通过 FIELDS 接口定义键名:
| 字段 | 说明 |
|---|---|
userId |
用户 ID |
dataScope |
数据权限范围 |
corpCode |
公司编码 |
corpList |
公司列表 |
deptList |
部门列表 |
isAdmin |
是否管理员 |
isSuperAdmin |
是否超管 |
token |
Token |
accountId |
账户 ID |
六、自动配置三件套¶
| 配置类 | 说明 |
|---|---|
DataPermissionAutoConfiguration |
afterPropertiesSet() 中创建 DataPermissionInterceptor,注入到所有 SqlSessionFactory |
DataPermissionConfigurationSupport |
实现 WebMvcConfigurer,注册 Spring MVC 拦截器 DataPermissionHandlerInterceptor |
PermissionBeanDefinition |
@Bean @ConditionalOnMissingBean 注册 DefaultDataScopeHandler、DefaultDataPermissionHandler |
自定义扩展¶
两个核心 Bean 都是 @ConditionalOnMissingBean,业务可自定义覆盖:
@Component
public class MyDataPermissionHandler implements DataPermissionHandler {
@Override
public Expression getSqlSegment(PlainSelect select) {
// 自定义 WHERE 条件生成逻辑
}
}
七、DataScopeAspect —— Controller 切面¶
cn.cnbm.bams.permission.aspect.DataScopeAspect
切点匹配 cn.cnbm.*.controller..*(1-4 层包深度)。
@Before 逻辑:
1. 若 spring.check-session.enabled=true 且方法无 @IgnoreSession,校验 session(无效抛 BussinessException)
2. 取 PermissionContext
3. isAdmin → ctx.clear()(管理员看全量)
4. 否则委托 DataScopeHandler.handle(point, context)
DefaultDataScopeHandler¶
| 方法 | 逻辑 |
|---|---|
processCommonDataPermission |
类/方法有 @CommonData → dataScope 设为 DATA_CORP_AND_PARENT |
processRequiredPermission |
类/方法有 @RequireDataScope(required=false) → ctx.clear()(不要权限) |
八、字段自动填充(协同)¶
common-permission 的 PermissionContextHolder 存有当前用户 userId,common-func 的 MyBatisPlusMetaObjectHandler 从中取值填充 BaseEntity 的 createdBy/updatedBy。
BamsMetaObjectHandler(本模块)已整体注释,被 common-func 的 MyBatisPlusMetaObjectHandler 取代。