Skip to content

RBAC 实现

VEF Framework 提供了内置的基于角色的访问控制(RBAC)实现。

RBAC 概念

  • 用户(User):系统的使用者
  • 角色(Role):权限的集合
  • 权限(Permission):执行特定操作的能力

配置 RBAC

实现 RolePermissionsLoader

go
package auth

import (
    "my-app/internal/modules/system/models"
    
    "github.com/ilxqx/vef-framework-go/auth"
    "github.com/ilxqx/vef-framework-go/orm"
)

type RolePermissionsLoaderImpl struct{}

func NewRolePermissionsLoader() auth.RolePermissionsLoader {
    return &RolePermissionsLoaderImpl{}
}

// LoadPermissions loads permissions for given roles
func (l *RolePermissionsLoaderImpl) LoadPermissions(roleIds []string) ([]string, error) {
    var permissions []string
    
    err := orm.DB().
        Table("role_permissions").
        Select("DISTINCT permission_token").
        Where("role_id IN ?", roleIds).
        Pluck("permission_token", &permissions).Error
    
    if err != nil {
        return nil, err
    }
    
    return permissions, nil
}

注册 Loader

go
// In your module setup
func NewAuthModule() fx.Option {
    return fx.Options(
        fx.Provide(
            auth.NewRolePermissionsLoader,
        ),
    )
}

角色模型

定义角色模型

go
package models

import "github.com/ilxqx/vef-framework-go/orm"

type Role struct {
    orm.Model
    Name        string       `gorm:"size:50;not null" json:"name"`
    Code        string       `gorm:"size:50;uniqueIndex;not null" json:"code"`
    Description string       `gorm:"size:200" json:"description"`
    IsActive    bool         `gorm:"default:true" json:"isActive"`
    Permissions []Permission `gorm:"many2many:role_permissions" json:"permissions,omitempty"`
}

type Permission struct {
    orm.Model
    Name   string `gorm:"size:50;not null" json:"name"`
    Token  string `gorm:"size:100;uniqueIndex;not null" json:"token"`
    Module string `gorm:"size:50" json:"module"`
}

type RolePermission struct {
    RoleId       string `gorm:"primaryKey"`
    PermissionId string `gorm:"primaryKey"`
}

用户角色关联

go
type User struct {
    orm.Model
    Username string `gorm:"size:50;uniqueIndex;not null" json:"username"`
    // ... other fields
    Roles    []Role `gorm:"many2many:user_roles" json:"roles,omitempty"`
}

type UserRole struct {
    UserId string `gorm:"primaryKey"`
    RoleId string `gorm:"primaryKey"`
}

角色管理 API

创建角色资源

go
package resources

import (
    "my-app/internal/modules/system/models"
    "my-app/internal/modules/system/payloads"
    
    "github.com/ilxqx/vef-framework-go/api"
    "github.com/ilxqx/vef-framework-go/apis"
)

type RoleResource struct {
    api.Resource
    apis.FindPageApi[models.Role, payloads.RoleSearch]
    apis.CreateApi[models.Role, payloads.RoleParams]
    apis.UpdateApi[models.Role, payloads.RoleParams]
    apis.DeleteApi[models.Role]
}

func NewRoleResource() api.Resource {
    resource := &RoleResource{
        Resource:    api.NewResource("smp/sys/role"),
        FindPageApi: apis.NewFindPageApi[models.Role, payloads.RoleSearch]().
            WithPreload("Permissions"),
        CreateApi:   apis.NewCreateApi[models.Role, payloads.RoleParams](),
        UpdateApi:   apis.NewUpdateApi[models.Role, payloads.RoleParams](),
        DeleteApi:   apis.NewDeleteApi[models.Role](),
    }
    
    // Custom API for assigning permissions
    resource.RegisterApiWithPermission(
        "assign_permissions",
        "role:assign_permissions",
        resource.AssignPermissions,
    )
    
    return resource
}

func (r *RoleResource) AssignPermissions(ctx fiber.Ctx, params struct {
    RoleId        string   `json:"roleId" validate:"required"`
    PermissionIds []string `json:"permissionIds" validate:"required"`
}) error {
    // Delete existing permissions
    if err := orm.DB().
        Where("role_id = ?", params.RoleId).
        Delete(&models.RolePermission{}).Error; err != nil {
        return api.Error(ctx, "Failed to update permissions")
    }
    
    // Insert new permissions
    for _, permId := range params.PermissionIds {
        rp := &models.RolePermission{
            RoleId:       params.RoleId,
            PermissionId: permId,
        }
        if err := orm.DB().Create(rp).Error; err != nil {
            return api.Error(ctx, "Failed to assign permission")
        }
    }
    
    // Clear permission cache
    auth.ClearAllPermissionCache()
    
    return api.Success(ctx, nil)
}

用户角色分配

go
func (r *UserResource) AssignRoles(ctx fiber.Ctx, params struct {
    UserId  string   `json:"userId" validate:"required"`
    RoleIds []string `json:"roleIds" validate:"required"`
}) error {
    // Delete existing roles
    if err := orm.DB().
        Where("user_id = ?", params.UserId).
        Delete(&models.UserRole{}).Error; err != nil {
        return api.Error(ctx, "Failed to update roles")
    }
    
    // Insert new roles
    for _, roleId := range params.RoleIds {
        ur := &models.UserRole{
            UserId: params.UserId,
            RoleId: roleId,
        }
        if err := orm.DB().Create(ur).Error; err != nil {
            return api.Error(ctx, "Failed to assign role")
        }
    }
    
    // Clear permission cache for user
    auth.ClearPermissionCache(params.UserId)
    
    return api.Success(ctx, nil)
}

权限树

构建权限树

go
type PermissionTree struct {
    Id       string            `json:"id"`
    Name     string            `json:"name"`
    Token    string            `json:"token"`
    Children []*PermissionTree `json:"children,omitempty"`
}

func BuildPermissionTree(permissions []models.Permission) []*PermissionTree {
    // Group by module
    moduleMap := make(map[string][]*PermissionTree)
    
    for _, p := range permissions {
        node := &PermissionTree{
            Id:    p.Id,
            Name:  p.Name,
            Token: p.Token,
        }
        moduleMap[p.Module] = append(moduleMap[p.Module], node)
    }
    
    // Build tree
    var tree []*PermissionTree
    for module, perms := range moduleMap {
        moduleNode := &PermissionTree{
            Id:       module,
            Name:     module,
            Token:    module + ":*",
            Children: perms,
        }
        tree = append(tree, moduleNode)
    }
    
    return tree
}

超级管理员

配置超级管理员

go
// Check if user is super admin
func (u *UserInfo) IsSuperAdmin() bool {
    for _, roleId := range u.RoleIds {
        if roleId == "super_admin" {
            return true
        }
    }
    return false
}

// Skip permission check for super admin
func (u *UserInfo) HasPermission(permission string) bool {
    if u.IsSuperAdmin() {
        return true
    }
    
    // Normal permission check
    for _, p := range u.Permissions {
        if p == permission {
            return true
        }
    }
    return false
}

下一步

基于 Apache License 2.0 许可发布