切换主题
用户、权限与系统管理
本文档介绍 CICD 自动打包部署平台的用户管理、角色与数据权限模型、登录与安全策略、操作审计,以及管理后台的菜单结构与页面清单。执行机 / 部署节点的接入见 执行机与部署节点,通知与告警见 通知。
平台的访问控制由两层构成:
| 层 | 载体 | 作用 |
|---|---|---|
| 角色 | sys_user.role(admin / user) | admin 不受数据权限约束,可管理执行机、资源、目标机、通知、模板、集群、用户与全部项目 |
| 数据权限 | sys_user_scope(按项目组或项目授权) | user 只在被授权的数据范围内可见与可操作,权限不足的对象等同不存在 |
用户管理
用户管理页面路径 /users,仅 admin 可见。
用户列表
分页查询(limit / offset,limit 默认 20、上限 200)。响应体:
json
{
"items": [
{
"id": 2,
"username": "alice",
"displayName": "Alice",
"role": "user",
"enabled": true,
"lastLoginAt": "2026-09-28 09:12:00",
"failedLoginCount": 0,
"lockedUntil": null,
"createdAt": "2026-09-01 10:00:00",
"updatedAt": "2026-09-28 09:12:00",
"scopeCount": 3
}
],
"total": 2,
"limit": 20,
"offset": 0
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
password(bcrypt 哈希)永不下发。scopeCount为该用户的数据权限项数(列表附带),用于「已授权 N 项」展示。lockedUntil为null表示未锁定;登录状态列的展示规则见下文「登录状态与解锁」。
用户详情
GET /api/users/:id(仅 admin):返回单个用户的基本信息(字段同列表项,password 哈希不下发)。前端「用户管理」列表的用户名与行内「详情」按钮进入 /users/:id 详情页,展示基本信息、登录状态与数据权限(GET /api/users/:id/scopes,按“授权对象 / 类型 / 权限档位 / 备注 / 授权时间”列出);admin 角色展示为「不受限制」。
新建用户
POST /api/users,请求体:
| 字段 | 必填 | 说明 |
|---|---|---|
username | 是 | 登录名,3–32 位,仅字母、数字、下划线与短横线 |
password | 是 | 6–72 位(bcrypt 只使用前 72 字节) |
displayName | 否 | 显示名称,留空时默认取 username |
role | 否 | admin 或 user,留空默认 user |
enabled | 否 | 是否启用,默认 true |
服务端用 bcrypt(cost=10)写入密码哈希,并写审计(user.create)。
编辑用户
PUT /api/users/:id,可更新显示名、角色、启用状态,并可选重置密码:
| 字段 | 说明 |
|---|---|
displayName | 显示名称,不能为空 |
role | 角色;不能取消自己的管理员角色 |
enabled | 启用状态;不能禁用自己 |
password | 传入非空即重置该用户密码(见下) |
未传的字段按空指针处理,保留原值;role / enabled 的改动立即生效(鉴权中间件每次请求都从数据库读取用户最新状态,JWT 只承载 userId 与 role)。
重置密码
用户列表行内的「重置密码」入口弹窗填写「新密码 + 确认密码」(6–72 位、两次一致),提交时走 PUT /api/users/:id 仅带 password 字段(displayName / role / enabled 不传,后端保留原值)。
- 重置密码会同时解除该用户的登录失败锁定,避免用户拿到新密码却仍被挡在门外。
- 重置不会使该用户已签发的 Token 失效,需其重新登录后才使用新密码。
- 审计明细中带
passwordReset: true。
启用 / 禁用
禁用用户(enabled=false)后:其已登录会话在下一次请求即被拒绝(401 用户已被禁用),登录接口也返回 403 用户已被禁用。启用后需重新登录。
删除用户
DELETE /api/users/:id:不能删除自己;删除时在同一事务内清理其全部数据权限记录。审计动作 user.delete。
登录状态与解锁
列表「登录状态」列展示登录失败锁定情况:
| 状态 | 展示 |
|---|---|
| 正常 | 正常 |
| 有失败但未锁定 | 失败 N 次 |
| 已锁定 | 已锁定 并附截止时间 |
锁定时行内出现「解锁」按钮,二次确认后调 POST /api/users/:id/unlock:清零 failed_login_count 并清空 locked_until,写审计 user.unlock。锁定与解锁策略见下文登录失败锁定。
修改自己的密码
任何登录用户都可在右上角「修改密码」自助改密:PUT /api/auth/password,请求体 {oldPassword, newPassword};原密码校验通过且新密码符合 6–72 位后写入 bcrypt 哈希,写审计 user.password。
角色与权限模型
角色档位
| 角色 | 说明 |
|---|---|
admin | 系统管理员,不受数据权限约束,直通全部数据与后台管理接口 |
user | 普通用户,只在其被授权的数据范围内可见与可操作 |
数据权限 sys_user_scope
按「项目组」或「项目」授权,两者可混用、可叠加:
| 字段 | 说明 |
|---|---|
user_id | 被授权用户 |
scope_type | 授权对象:group(项目组)/ project(项目) |
scope_id | 项目组 ID 或项目 ID |
permission | view(只读)/ operate(可触发)/ manage(可管理) |
remark | 备注(为什么授权,便于审计) |
权限档位高低为 manage > operate > view(PermissionRank:view=1 < operate=2 < manage=3):
| 档位 | 含义 |
|---|---|
view | 只读:可看项目配置、任务列表与日志、下载产物 |
operate | 可触发:仅可运行任务、Git 连通性测试、放行/驳回人工卡点 |
manage | 可管理:可编辑项目配置、管理任务与成员(含触发) |
生效规则
admin直通全部数据;- 某用户对某项目的有效权限按「项目级覆盖分组级」计算:存在
project直接授权时仅以项目授权为准(可对组内个别项目降权或提权);无项目直接授权时才取所属项目组的group授权;两者均无 → 无权限; - 同一对象存在多个授权项时取较高者(
manage>operate>view); - 无任何授权 → 不可见:列表不返回,详情 / 日志 / SSE 返回 404(而非 403),避免探测资源是否存在;
- 项目组授权对组内后续新增的项目自动生效(每次按项目当前所属分组实时计算,不做快照)。
鉴权中间件规则
权限校验与路由分组注册的约定如下:
| 中间件 | 要求 |
|---|---|
RequireLogin() | 校验 Authorization: Bearer <JWT>,并从数据库加载用户最新状态(禁用 / 改角色立即生效) |
RequireLoginOrTicket() | 同上,无请求头时改认查询参数 ?ticket=<短期下载票据>;仅用于任务产物下载(浏览器原生下载带不了请求头)。票据只替代请求头,项目 / 任务权限照常校验,有效期 2 分钟且限定单个产物与任务 |
RequireAdmin() | 角色必须为 admin(执行机、资源、目标机、模板、通知、用户、审计、集群) |
RequireProject(perm) | 对路径 :id 项目的有效权限档位 ≥ perm;admin 直通;无授权 404、档位不足 403 |
RequireGroup(perm) | 对路径 :id 项目组的有效权限档位 ≥ perm;仅用于「谁能管理该分组的成员」 |
RequireManager() | 用户在任一项目组 / 项目上拥有 manage 授权(admin 视为 true);仅用于项目配置页所需的只读下拉数据 |
ProjectScopeFilter(ctx) | 列表类查询按 project_id IN (可访问项目集合) 过滤;admin 不加条件 |
缓存(默认关闭):auth.scopeCacheSeconds 默认 0,权限点每次直查 sys_user_scope;显式设为 > 0 才启用按 user_id 的进程内缓存,并在授权写入路径主动清除。进程内缓存集群下只能失效本实例,其它实例最长滞后该秒数,故默认关闭。
授权维护入口
两处入口并存,操作同一张 sys_user_scope:
- 用户管理 → 数据权限(
GET/PUT /api/users/:id/scopes,仅admin):以「人」为中心,保存时整体覆盖该用户的全部授权项(事务内先删后插),请求体{"scopes":[{scopeType,scopeId,permission,remark}]}; - 项目分组列表 / 项目列表 → 行内「成员」按钮 → 抽屉(
/api/groups/:id/members、/api/projects/:id/members):以「对象」为中心,逐个增删成员并设置权限与备注。可操作者 =admin或对该对象有manage权限的用户,即「只有可管理的人才能添加成员」。可选用户清单(仅enabled=1且非admin)由接口随成员列表一并返回,普通用户无需访问用户管理接口。
两处入口写入后都会主动清除被授权用户的权限缓存(仅作用于处理该请求的实例)。
与登录态的关系
- JWT 只承载
userId与role,数据权限不进 JWT(避免授权变更后旧 Token 仍带旧权限),每次请求按userId查询(默认不缓存)。 - 前端登录后由
GET /api/auth/profile拿到{user, scopes},用于菜单与按钮显隐;前端隐藏不等于鉴权,后端逐接口校验。
登录与安全
JWT 登录态
- 登录接口
POST /api/auth/login校验通过后签发 JWT(HS256,默认 12h 过期,可由auth.tokenExpireHours调整),响应{token, user}。 - Claims 只含
uid(用户 ID)与role;数据权限不进 Token。 - 请求头
Authorization: Bearer <JWT>;解析失败或过期返回401 未登录或登录已过期。 - 退出登录
POST /api/auth/logout:前端清理 Token,服务端仅写审计logout(JWT 无服务端黑名单)。
登录失败锁定
由 config.yaml 的 auth 段控制,默认开启(详见 配置说明):
| 配置项 | 默认 | 说明 |
|---|---|---|
auth.maxLoginFailures | 5 | 连续登录失败达到该次数即锁定账号;显式配 0 表示关闭该策略 |
auth.loginLockMinutes | 30 | 锁定时长(分钟),到点自动解锁 |
- 计数口径:只统计「密码错误」(用户不存在、账号禁用不计入);登录成功、管理员重置密码、管理员手动解锁都会清零。
- 锁定期内即使密码正确也拒绝登录,只能等锁定期结束或由管理员解锁。
- 计数落在
sys_user.failed_login_count/locked_until两列(跨重启有效,多实例一致);锁定期到期后再次输错从1重新计数,不累计历史。 - 累加在存储层事务内完成(
SELECT ... FOR UPDATE),避免多实例并发下丢更新而绕过锁定。
接口返回:
| 情形 | 状态码 | 提示 |
|---|---|---|
| 未达阈值 | 401 | 用户名或密码错误,还可尝试 N 次 |
| 达到阈值(本次触发锁定) | 403 | 密码连续错误 N 次,账号已锁定 N 分钟 |
| 锁定期内再次请求 | 403 | 密码错误次数过多,账号已锁定至 <时间> |
锁定与解锁都会写审计(login 动作的 reason、user.unlock)。
密码存储
用户密码用 bcrypt(cost=10) 单向哈希存储在 sys_user.password,不可逆;遗忘密码可用管理端二进制 -hash-password 生成新哈希后手动更新(见 安装部署)。
敏感字段加密
库内敏感字段经 AES-256-GCM 加密,密文形如 enc:v1:<base64(nonce||cipher)>,密钥来自 auth.encryptKey(必须 32 字节,生产环境改用环境变量 CICD_ENCRYPT_KEY)。加密 / 解密由管理端统一处理,存储层不感知加解密。接口永不回显密文,未修改敏感字段时提交 null 或掩码 ****** 表示保留原值。
| 数据 | 位置 | 处理 |
|---|---|---|
| 用户密码 | sys_user.password | bcrypt(cost=10),不可逆 |
| 执行机 / 节点 Token | agent.token_hash | SHA-256,明文只在生成时返回一次 |
| SSH 密码 / 私钥 / 口令、切换用户密码 | target_server.config | AES-256-GCM |
| Git HTTPS 密码 / Token | project.config.git.password | AES-256-GCM |
| Webhook 验签密钥 | project.config.webhook.secret | AES-256-GCM |
| 项目 / 分组任务变量敏感项 | config.env[].value | AES-256-GCM;日志落库前替换为 ****** |
| 通知 Secret / SMTP 密码 | notify_channel.config | AES-256-GCM |
| 镜像仓库登录密码 | docker_registry.password | AES-256-GCM |
| 执行端 Git 私钥 | 执行机本地 {gitKeyRoot} | 不上报、不入库,仅本地文件 0600 |
审计日志
记录范围
平台在用户、执行机、节点、项目、分组、资源、目标机、仓库、通知、模板、任务、Webhook、集群等关键操作上写入审计(audit_log)。主要动作(action):
| 分类 | 动作 |
|---|---|
| 登录 | login(含失败原因)、logout、user.password |
| 用户 | user.create、user.update、user.delete、user.scope、user.unlock |
| 权限成员 | scope.member(项目组 / 项目维度增删改成员) |
| 执行端 | agent.create、agent.update、agent.delete、agent.token.reset、agent.key.rotate、node.create、node.update、node.delete、node.token.reset、workspace.clean |
| 配置 | project.create、project.update、project.delete、group.save、resource.save、registry.save、target.save、template.save、content_template.save |
| 任务 | task.run、task.cancel、task.retry、task.promote、task.approve、task.reject |
| 通知 / Webhook | notify.save、notify.test、webhook.update、webhook.replay |
| 集群 | cluster.save |
每条记录含操作人(username / role)、目标类型(target_type)与目标 ID、明细(detail,JSON,敏感值已掩码)、来源 IP(ip)与时间(created_at)。审计写入失败不影响业务,仅记录服务端日志。
查询
审计日志页面路径 /audits,仅 admin 可见。GET /api/audits 支持:
| 参数 | 说明 |
|---|---|
username | 按操作人精确过滤 |
action | 按动作精确过滤(如 login、user.update) |
from / to | 时间范围(2006-01-02 15:04:05) |
limit / offset | 分页(默认 20、上限 200) |
响应 {items, total, limit, offset},按 id DESC 返回。
集群
集群页面路径 /cluster(系统 → 集群),仅 admin 可见,用于管理端多实例部署下的领导者租约与实例状态展示。
GET /api/cluster返回:degraded(是否退化为单实例)、leaseSeconds/leaseRemainSecs(租约时长与剩余)、alertChannelIds(集群事件告警渠道)、self{instanceId,leader}、leader{instanceId,expireAt}、instances[](实例标识 / 主机名 / IP / 端口 / 版本 / 角色 / 在线状态)。- 实例列表来自各实例每 10 秒的心跳注册,超 30 秒无心跳视为离线;角色以租约实时校正。
PUT /api/cluster/settings保存集群设置,当前仅「集群事件告警渠道」{alertChannelIds:[]}。渠道引用「通知渠道」页维护的渠道 ID,保存时校验存在性,五种渠道均可选用。写审计cluster.save。- 未执行升级脚本(
cluster_*表缺失)时退化为单实例模式而非启动失败,集群信息页仅展示本实例。
管理后台菜单结构与页面清单
菜单每级(含可展开的父级)都配 Element Plus 图标,图标在各页面按需局部引入。菜单与按钮按 scopes 显隐:admin 显示全部菜单;user 隐藏执行机 / 资源 / 目标机 / 通知 / 用户 / 审计 / 集群等 admin 专属菜单。
概览 /overview 仪表盘:执行机状态、任务统计、队列长度、最近任务
执行机 /agents 列表·Token 生成·启停·标签·探测·Git 公钥与工作目录;同页含「部署节点」区
项目 /projects 项目列表·配置(manage)·触发·分组·成员(manage)
└ 项目列表 /projects 同上
└ 项目模板 /templates 模板列表·预览;新建 / 编辑(admin)
└ 内容模板 /content-templates 定义启停脚本等文件内容;按项目分组授权;新建 / 编辑(admin)
└ 项目分组 /projects/groups 分组 CRUD(admin)·成员(manage)·详情(/projects/groups/:id)
资源 /resources 环境资源(一套资源可关联多台执行机,可按执行机过滤)
└ 环境资源 /resources JDK / Go / Node / Docker 路径与关联执行机
└ 镜像仓库 /registries 私有仓库登录凭据(Docker Hub / 自建 registry)
└ 目标服务器 /targets 部署目标机:SSH 直连(密码 / 密钥文件)或绑定部署节点
通知渠道 /notify/channels 渠道维护与测试发送(仅 admin);通知内容内置
任务 /tasks 任务历史·重试·取消
└ 任务列表 /tasks
└ 运行 /tasks/run 选择项目 → 选范围 → 实时日志
└ 队列 /tasks/queue 排队任务:排队原因·位置·置顶·取消
Webhook /webhooks/deliveries 投递记录:匹配结论·原因·重放
系统(admin) /users 用户管理·数据权限(项目组 / 项目授权)
└ 用户管理 /users
└ 审计日志 /audits
└ 集群 /cluster 部署模式·领导者租约·实例列表·告警渠道1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
各页面职责与接口对应:
| 页面 | 路径 | 主要功能 | 相关接口 |
|---|---|---|---|
| 概览 | /overview | 执行机在线数、任务统计、队列长度、最近任务(按数据权限过滤) | GET /api/overview |
| 执行机 | /agents | 列表 / 新建(一次性 Token)/ 启停 / 标签 / 探测 / Git 公钥 / 工作目录;同页「部署节点」区 | 见 执行机与部署节点 |
| 项目列表 | /projects | 列表 / 详情 / 复制 / 导入导出 / 初始化示例 | 见 项目与项目组 |
| 项目配置 | /projects/:id/config | 构建步骤、Git、任务变量、部署文件、停/部/启步骤、Webhook、人工卡点;需 manage | GET/PUT /api/projects/:id* |
| 项目模板 | /templates | 模板列表 / 预览 / 新建 / 编辑 / 导入导出 | /api/templates* |
| 内容模板 | /content-templates | 定义启停脚本等文件内容,分配项目分组 | /api/content-templates* |
| 项目分组 | /projects/groups | 分组 CRUD、分组配置(任务变量 / 目标服务器 / 通知)、成员 | /api/groups* |
| 环境资源 | /resources | JDK / Go / Node / Docker 路径与关联执行机 | /api/resources* |
| 镜像仓库 | /registries | 私有仓库登录凭据 | /api/registries* |
| 目标服务器 | /targets | SSH 直连或绑定部署节点 | /api/targets* |
| 通知渠道 | /notify/channels | 渠道维护与测试发送;通知内容内置 | /api/notify/* |
| 任务列表 | /tasks | 任务历史 / 详情 / 重试 / 取消 / 队列操作 | 见 任务与队列 |
| 运行 | /tasks/run | 选项目 → 选范围 → 实时日志(SSE) | POST /api/projects/:id/run、GET /api/tasks/:id/stream |
| 队列 | /tasks/queue | 排队任务、置顶、调整优先级、取消 | `POST /api/tasks/:id/promote |
| 投递记录 | /webhooks/deliveries | Webhook 投递结论、原因、重放 | 见 Webhook |
| 用户管理 | /users、详情 /users/:id | 创建 / 编辑 / 删除、启用禁用、重置密码、登录状态与解锁、数据权限(详情页展示) | /api/users*、/api/auth/* |
| 审计日志 | /audits | 操作审计查询 | GET /api/audits |
| 集群 | /cluster | 部署模式、领导者租约、实例列表、集群告警渠道 | GET /api/cluster、PUT /api/cluster/settings |
前端路由守卫约定:
meta.admin = true的路由仅admin可进(执行机、资源、仓库、目标机、通知、用户、审计、集群、分组表单、项目新建等);meta.manage = true的路由(项目配置)对任一manage授权用户开放,页面内写操作仍逐接口校验;meta.public = true仅登录页;- 非菜单路由(详情页 / 表单页)用
meta.menu声明归属的一级菜单,保证左侧菜单保持选中。