跳转至

07-数据权限 common-permission

📄 创建: ZCode AI 2026-08-14 · 修改: JimWb 2026-08-14

https://doc.xpku.com/raw/tech/backend/bams-sdk/07-common-permission.md

common-permission(13 个类)基于 MyBatis 拦截器实现行级数据权限:在 SQL 执行前动态拼接 WHERE 条件,按公司/部门/本人等维度自动过滤数据。


一、开启数据权限

启动类加注解

@SpringBootApplication
@EnableDataPermission  // 开启数据权限框架
public class MyApp { ... }

@EnableDataPermissioncn.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 —— 关闭权限

@RequireDataScope(required = false)  // 这个方法不要数据权限
public Object publicList() { ... }

@IgnoreSession —— 跳过 session 校验

@IgnoreSession  // 不校验 session,也不加数据权限
@PostMapping("/public/api")
public Object publicApi() { ... }

@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 注册 DefaultDataScopeHandlerDefaultDataPermissionHandler

自定义扩展

两个核心 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. isAdminctx.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 取代。


相关文档