跳转至

权限系统架构

Remilia 的权限系统分为两个层次,包名相同但职责完全不同。初次使用时请先阅读本文档。


架构全景

┌───────────────────────────────────────────────────────────────┐
│                       Bot / 插件开发者                          │
│                                                               │
│  plugin.Service[permission.Plugin](ctx, "permission")            │
│  perm.CheckPermission(ctx, Permission{"admin","kick"})        │
└─────────────────────────┬─────────────────────────────────────┘
                          │ 使用
┌─────────────────────────▼─────────────────────────────────────┐
│            builtin/core/permission  (管理插件层)               │
│                                                               │
│  · Plugin struct — 插件 API 对象                               │
│  · /acl add/rm/list 命令                                       │
│  · VerificationManager — 身份验证管理                          │
│  · AccessControlList — ACL 黑白名单                            │
│  · StorageBackend — 可选持久化接口                             │
└─────────────────────────┬─────────────────────────────────────┘
                          │ 依赖
┌─────────────────────────▼─────────────────────────────────────┐
│               core/permission  (内核层)                        │
│                                                               │
│  · Permission — (resource, action) 权限对,支持通配符           │
│  · Role       — 权限集合                                       │
│  · Manager    — 用户↔角色↔权限映射表(线程安全)               │
│  · Provider   — 可选外部角色数据源接口                         │
│                                                               │
│  零依赖:不引入插件系统、不引入 core/context                    │
└───────────────────────────────────────────────────────────────┘

core/permission:权限原语(内核层)

包路径github.com/KomeiDiSanXian/remilia/core/permission

职责:定义 RBAC 的数据结构和算法,不暴露任何命令或 HTTP 接口。

特点: - 零外部依赖,可在非 Bot 场景(HTTP 服务、CLI 工具)中独立使用 - Manager 是线程安全的内存权限表 - 支持通配符权限("*" 匹配任意资源/动作,"prefix:*" 匹配前缀) - 通配符只在授权侧展开(2026-07 收紧):请求侧(HasPermission 的 target 参数) 出现的 "*" 一律按字面值处理——否则把用户可控字符串透传进权限检查的调用方 可被 "*" 探测/绕过。另外 Provider 外部查询移到锁外执行,慢查询不再阻塞写操作

通过 Context 访问

core/context*Manager 存储在 typed-extension 中,提供两个便捷方法:

// 在中间件中注入 Manager
ctx.SetPermissionManager(myManager)

// 在 handler 中获取 Manager
pm := ctx.GetPermissionManager()
if pm != nil {
    allowed := pm.CheckPermission(userID, eventctx.NewPermission("message", "send"))
}

builtin/core/permission:权限管理插件(插件层)

包路径github.com/KomeiDiSanXian/remilia/builtin/core/permission

职责:基于 core/permission 构建,向 Bot 用户暴露运行时权限管理命令。

提供的功能: - /acl add <user> <role> — 为用户授予角色 - /acl rm <user> <role> — 撤销用户角色 - /acl list <user> — 查询用户角色和权限 - 身份验证(VerificationManager) - ACL 黑白名单(AccessControlList) - 可选对接 plugins/core/storage 实现持久化

注册方式

// 仅注册权限管理插件(内存模式,重启丢失)
pm.Register(permission.New())

// 带持久化(先注册 storage 插件)
pm.Register(storage.New())
pm.Register(permission.New()) // 自动通过 Try[storage.Plugin] 获取存储后端

在其他插件中使用

import permission "github.com/KomeiDiSanXian/remilia/builtin/core/permission"

Setup: func(ctx *plugin.SetupContext) (any, error) {
    perm := plugin.Service[permission.Plugin](ctx, "permission")
    ctx.Reg.RegisterCommand(dto.C2CMessageCreate, "/kick").Handle(func(c *eventctx.Context) error {
        if !perm.CheckPermission(c, eventctx.NewPermission("admin", "kick")) {
            c.ReplyText("权限不足")
            return nil
        }
        // ...
        return nil
    })
    return p, nil
},

常见误区

问题 正确理解
为什么有两个 permission 包? 一个是数据结构(内核),一个是功能插件(应用层),职责分离
我只需要在代码中做权限检查,用哪个? core/permissionManager,通过 ctx.GetPermissionManager() 访问
我需要用户在聊天中管理权限,用哪个? 注册 builtin/core/permission 插件,通过 /acl 命令管理
两者能混用吗? 可以。插件层内部使用内核层的 Manager,通过 ctx.SetPermissionManager 注入到 Context