Skip to content

数据权限

youlai-gin 支持数据权限控制,基于 GORM Scope(Scopes) 对查询自动追加数据范围过滤条件。

核心能力:

  • ApplyDataScope:对 GORM 查询应用数据权限过滤
  • DataScopeFilter:GORM Scope 函数,链式调用

与 youlai-boot 对齐

特性youlai-bootyoulai-gin
多角色策略并集(OR)策略并集(OR)策略
自定义字段名deptIdColumnName, userIdColumnNameDeptIDColumn, UserIDColumn
超级管理员豁免ROOT 角色跳过ROOT 角色跳过
注解机制@DataPermission无(需手动调用)

整体架构图

数据范围枚举 DataScopeEnum

位置:internal/common/permission/datascope/data_scope.go

go
const (
    DataScopeAll             = 1 // 全部数据 - 最高权限
    DataScopeDeptAndChildren = 2 // 部门及子部门
    DataScopeDept            = 3 // 本部门
    DataScopeSelf            = 4 // 仅本人
    DataScopeCustom          = 5 // 自定义部门
)

注意:枚举值越小,权限范围越大。

核心组件

1. DataPermissionConfig 配置

go
type DataPermissionConfig struct {
    DeptAlias    string // 部门表别名
    DeptIDColumn string // 部门ID字段名,默认 "dept_id"
    UserAlias    string // 用户表别名
    UserIDColumn string // 用户ID字段名,默认 "create_by"
}

2. RoleDataScope 模型

支持多角色数据权限,每个角色独立维护数据权限信息:

go
type RoleDataScope struct {
    RoleCode      string  // 角色编码
    DataScope     int     // 数据权限范围值 (1-5)
    CustomDeptIDs []int64 // 自定义部门ID列表(CUSTOM 时使用)
}

3. 多角色并集策略

核心逻辑位于 buildUnionCondition

go
// 构建多角色并集查询条件
func buildUnionCondition(db *gorm.DB, dataScopes []auth.RoleDataScope, 
    deptColumn, userColumn string, userID int64) *gorm.DB {
    
    // 收集各角色的条件
    var orConditions []string
    var args []interface{}
    
    for _, ds := range dataScopes {
        cond, condArgs := buildRoleCondition(ds, deptColumn, userColumn, userID)
        if cond != "" {
            orConditions = append(orConditions, cond)
            args = append(args, condArgs...)
        }
    }
    
    // 使用 OR 连接所有条件
    // (条件1) OR (条件2) OR (条件3)
    ...
}

各权限类型生成的 WHERE 条件

权限类型生成的 WHERE 条件
ALL无过滤
DEPT_AND_CHILDRENdept_id IN (部门及子部门ID列表)
DEPTdept_id = 当前部门ID
SELFcreate_by = 当前用户ID
CUSTOMdept_id IN (自定义部门ID列表)

使用示例

基本使用

go
import (
    "youlai-gin/internal/common/auth"
    "youlai-gin/internal/common/permission/datascope"
)

// 方式一:ApplyDataScope
func ListUsers(c *gin.Context) {
    user := auth.MustGetUser(c)
    db := database.DB.Model(&User{})
    
    // 应用数据权限过滤
    db = datascope.ApplyDataScope(db, user, datascope.DataPermissionConfig{
        DeptIDColumn: "dept_id",
        UserIDColumn: "create_by",
    })
    
    var users []User
    db.Find(&users)
    c.JSON(200, users)
}

// 方式二:DataScopeFilter (GORM Scope)
func ListUsers(c *gin.Context) {
    user := auth.MustGetUser(c)
    
    var users []User
    database.DB.Scopes(datascope.DataScopeFilter(user, datascope.DataPermissionConfig{
        DeptIDColumn: "dept_id",
        UserIDColumn: "create_by",
    })).Find(&users)
    
    c.JSON(200, users)
}

自定义字段名

go
// 任务表使用不同的字段名
func ListTasks(c *gin.Context) {
    user := auth.MustGetUser(c)
    
    var tasks []Task
    database.DB.Scopes(datascope.DataScopeFilter(user, datascope.DataPermissionConfig{
        DeptIDColumn: "org_id",
        UserIDColumn: "creator_id",
    })).Find(&tasks)
    
    c.JSON(200, tasks)
}

表别名支持

go
// JOIN 查询时使用表别名
func ListUsersWithDept(c *gin.Context) {
    user := auth.MustGetUser(c)
    
    var users []User
    database.DB.Table("sys_user u").
        Select("u.*, d.name as dept_name").
        Joins("LEFT JOIN sys_dept d ON u.dept_id = d.id").
        Scopes(datascope.DataScopeFilter(user, datascope.DataPermissionConfig{
            DeptAlias:    "u",
            DeptIDColumn: "dept_id",
            UserAlias:    "u",
            UserIDColumn: "create_by",
        })).
        Find(&users)
    
    c.JSON(200, users)
}

// 生成的 WHERE 条件:
// WHERE (u.dept_id IN (1, 2, 3)) OR (u.create_by = 100)

多角色数据权限

用户拥有多个角色时,采用并集策略(OR 连接)

go
// 用户拥有两个角色:
// - 部门管理员: dataScope = 3 (本部门)
// - 普通用户: dataScope = 4 (本人)

// 生成的 WHERE 条件:
// WHERE (dept_id = 10) OR (create_by = 100)

// 用户可以看到本部门的数据 OR 自己创建的数据

注意事项

  1. ROOT 角色自动跳过:拥有 ROOT 角色的用户不受数据权限限制
  2. 必须手动调用:在查询前显式调用 ApplyDataScopeScopes(DataScopeFilter(...))
  3. 并集策略:多角色时权限取并集(OR)
  4. 表别名:JOIN 查询时需指定正确的表别名,避免字段歧义

数据库设计

sys_role 表

sql
CREATE TABLE `sys_role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `code` varchar(32) NOT NULL COMMENT '角色编码',
  `data_scope` tinyint COMMENT '数据权限(1-全部 2-部门及子部门 3-本部门 4-本人 5-自定义)',
  PRIMARY KEY (`id`)
);

sys_role_dept 表(自定义权限)

sql
CREATE TABLE `sys_role_dept` (
  `role_id` bigint NOT NULL COMMENT '角色ID',
  `dept_id` bigint NOT NULL COMMENT '部门ID',
  UNIQUE INDEX `uk_roleid_deptid`(`role_id`, `dept_id`)
);

权限模块结构

internal/common/permission/
├── datascope/
│   └── data_scope.go                # ApplyDataScope / DataScopeFilter
└── service/
    └── permission_service.go        # GetUserDataScopes / HasAllDataScope

基于 MIT 许可发布 · 由 ❤️ 和 ☕ 驱动 · 支持作者