XYGo Admin 这轮更新,把中后台最容易返工的权限和 CRUD 链路重新理顺了

2026-07-08 42 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

中后台项目最怕的不是页面多,而是权限、菜单、接口、表单四条线各写各的:前端按钮藏了,接口还能调;菜单配好了,接口权限漏了;CRUD 页面生成出来了,表单和权限还要再补一遍。XYGo Admin 最近这轮调整,重点就是把这些容易返工的地方重新串起来,尤其是 RBAC 权限链路和 CRUD 代码生成流程。

RBAC 不再只停留在“前端隐藏按钮”

这次更新里最值得关注的是 RBAC 权限链路的分层处理:菜单权限、按钮权限、接口权限分开看,但不能割裂。

很多后台系统早期会这样做:

  • 菜单权限决定用户能不能看到某个页面;
  • 按钮权限决定用户能不能看到“新增”“删除”“导出”;
  • 接口权限在后端中间件里校验请求是否允许。

问题在于,如果只做前两层,安全边界其实还在浏览器里。用户看不到删除按钮,不代表他不能手写一个 HTTP 请求去打删除接口。所以 XYGo Admin 这次强调将菜单、按钮、接口权限分层处理,实际是在把权限从“界面控制”推进到“服务端强制校验”。

可以这样理解权限链路:

用户 -> 角色 -> 菜单权限 -> 页面可见
               -> 按钮权限 -> 操作入口可见
               -> 接口权限 -> 请求是否真正放行

这条链路的关键不是多存几张表,而是每一次生成、配置、发布时都能少漏一环。

CRUD 生成流程要和权限一起长出来

来源摘要提到,后台模块在生成时会同步考虑权限、菜单、接口和表单页面。这是中后台工程里很现实的改动。

一个典型 CRUD 模块并不只是 list/create/update/delete 四个接口。它通常还包括:

  • 左侧菜单入口;
  • 列表页路由;
  • 新增、编辑、删除、查询、导出等按钮;
  • 后端接口路由;
  • 表单字段、校验规则、默认值;
  • 角色授权时可勾选的权限点。

如果代码生成器只生成页面和接口,权限点靠人工补,后期返工概率很高。尤其是多人协作时,A 生成页面,B 加接口,C 配菜单,D 再补角色权限,任何一步漏掉都会变成测试环境里的“为什么我看不到页面”或“为什么普通账号能删数据”。

这次调整的方向更像是让 CRUD 生成流程产出一组配套资产,而不是一堆孤立文件。

可以这样实践:用权限清单驱动菜单、按钮和接口

下面是一个可改造的最小示例,用 YAML 描述一个中后台模块的权限清单,再用一个 Python 脚本生成菜单、按钮和接口权限的种子数据。它不是 XYGo Admin 的官方接口,只是一个适合团队落地这类思路的实践样例。

先创建 permissions.yaml

module: system_user
name: 用户管理
menu:
  path: /system/users
  title: 用户管理
  permission: system:user:view
buttons:
  - key: create
    title: 新增用户
    permission: system:user:create
  - key: update
    title: 编辑用户
    permission: system:user:update
  - key: delete
    title: 删除用户
    permission: system:user:delete
apis:
  - method: GET
    path: /api/system/users
    permission: system:user:view
  - method: POST
    path: /api/system/users
    permission: system:user:create
  - method: PUT
    path: /api/system/users/{id}
    permission: system:user:update
  - method: DELETE
    path: /api/system/users/{id}
    permission: system:user:delete

安装依赖并生成 SQL:

python -m venv .venv
source .venv/bin/activate
pip install pyyaml
python generate_permissions.py

generate_permissions.py

import yaml
from pathlib import Path

config = yaml.safe_load(Path("permissions.yaml").read_text(encoding="utf-8"))

module = config["module"]
menu = config["menu"]
buttons = config.get("buttons", [])
apis = config.get("apis", [])

sql = []

sql.append(
    "INSERT INTO admin_menu(module, title, path, permission) "
    f"VALUES ('{module}', '{menu['title']}', '{menu['path']}', '{menu['permission']}');"
)

for button in buttons:
    sql.append(
        "INSERT INTO admin_permission(module, type, name, permission) "
        f"VALUES ('{module}', 'button', '{button['title']}', '{button['permission']}');"
    )

for api in apis:
    sql.append(
        "INSERT INTO admin_permission(module, type, name, permission, method, path) "
        f"VALUES ('{module}', 'api', '{api['method']} {api['path']}', "
        f"'{api['permission']}', '{api['method']}', '{api['path']}');"
    )

Path("permissions.sql").write_text("\n".join(sql) + "\n", encoding="utf-8")
print("generated permissions.sql")

运行后会得到类似:

INSERT INTO admin_menu(module, title, path, permission) VALUES ('system_user', '用户管理', '/system/users', 'system:user:view');
INSERT INTO admin_permission(module, type, name, permission) VALUES ('system_user', 'button', '新增用户', 'system:user:create');
INSERT INTO admin_permission(module, type, name, permission) VALUES ('system_user', 'button', '编辑用户', 'system:user:update');
INSERT INTO admin_permission(module, type, name, permission) VALUES ('system_user', 'button', '删除用户', 'system:user:delete');
INSERT INTO admin_permission(module, type, name, permission, method, path) VALUES ('system_user', 'api', 'GET /api/system/users', 'system:user:view', 'GET', '/api/system/users');

这个例子的重点是:权限不靠开发者临场想,而是从模块定义里统一生成。实际接入 XYGo Admin 时,可以把这类清单接到项目已有的代码生成流程里,让模块生成时同时落下菜单、按钮、接口权限和页面骨架。

后端接口必须独立校验权限

前端按钮权限依然有价值,它能减少误操作,也能让界面更清爽。但后端接口权限必须单独存在。

可以这样实践一个很小的 Express 中间件,用权限码保护接口:

const express = require("express");
const app = express();

function requirePermission(permission) {
  return function (req, res, next) {
    const permissions = req.headers["x-user-permissions"]
      ? req.headers["x-user-permissions"].split(",")
      : [];

    if (!permissions.includes(permission)) {
      return res.status(403).json({ message: "permission denied", permission });
    }

    next();
  };
}

app.get(
  "/api/system/users",
  requirePermission("system:user:view"),
  (req, res) => {
    res.json([{ id: 1, name: "admin" }]);
  }
);

app.delete(
  "/api/system/users/:id",
  requirePermission("system:user:delete"),
  (req, res) => {
    res.json({ deleted: req.params.id });
  }
);

app.listen(3000, () => {
  console.log("server listening on http://localhost:3000");
});

保存为 server.js 后运行:

npm init -y
npm install express
node server.js

测试无权限访问:

curl -i http://localhost:3000/api/system/users

测试有权限访问:

curl -i \
  -H 'x-user-permissions: system:user:view,system:user:create' \
  http://localhost:3000/api/system/users

真实项目里权限通常来自登录态、JWT、Session 或网关注入的用户上下文,而不是 HTTP Header。这里用 Header 只是为了让示例可以直接复制运行。

落地时要盯住三件事

这类调整最怕“生成时很完整,维护时又散掉”。建议团队采用时重点检查三点。

第一,权限码命名要稳定。比如统一使用 domain:resource:action,不要一会儿是 system:user:add,一会儿是 user:create。权限码一旦进入角色配置和审计日志,后续改名成本很高。

第二,代码生成不能绕过人工审查。CRUD 生成能减少重复劳动,但字段权限、敏感操作、批量删除、导出接口这些地方仍然要由开发者确认。生成器适合铺路,不适合替你判断业务风险。

第三,接口权限要进入测试。至少要覆盖“有权限通过、无权限拒绝、前端隐藏但接口不可越权”这几类用例。否则 RBAC 链路看起来完整,实际上仍可能在某个接口上漏网。

XYGo Admin 这轮更新的价值,不在于多了几个配置项,而在于把中后台项目最容易脱节的几条线拉回同一个流程里。对团队来说,真正的收益是少返工、少漏配、少在测试阶段追权限问题。采用时不要只看生成出来的页面漂不漂亮,更要看权限、菜单、接口、表单是不是能一起被创建、被审查、被维护。


相关推荐