文档权限:补充三角色端到端验收证据

This commit is contained in:
wangzhibiao committed 2026-09-16 10:48:13 +08:00
1 parent c271c11b29
commit 7ae7ad8d2a
31 files changed
+501

No files matched your search

@@ -0,0 +1,21 @@
# SYFJGYD学员管理系统 R.2B Wave 3 E2E 验收证据包
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA` (数据库 127.0.0.1:4321/training, Redis 127.0.0.1:6379, 后端 8090, 前端 8080)
- **迁移验证复用基准 (MIGRATION_VALIDATION_RUN)**: `RBAC_E2E_R2_20260916_100800` (`MIGRATION_VALIDATION_REUSED = YES`)
- **端到端验收状态 (E2E_R2_STATUS)**: `PASS`
- **高清截图总数 (SCREENSHOT_COUNT)**: `21` (存放于 `screenshots/`)
## 交付文档目录索引 (>=10 项完整清单)
1. [README.md](README.md): 交付包综述与验收元数据索引
2. [report.md](report.md): R.2 三角色认证、功能权限与数据权限 E2E 综合验收报告
3. [report.html](report.html): 离线可视化交互式交付报告 (集成 21 张全链路无损截图画廊)
4. [run-data.json](run-data.json): 42 项机器可读验收指标字典 (JSON 标准格式)
5. [role-matrix.md](role-matrix.md): 三角色 (Admin / Teacher / Student) 及特例主体功能、路由与数据矩阵
6. [idor-results.md](idor-results.md): 横向越权 (IDOR) 与垂直越权全链路防御渗透记录
7. [account-binding-results.md](account-binding-results.md): 管理员开户与业务身份绑定正负向及安全边界测试记录
8. [migration-validation.md](migration-validation.md): 004/005 增量数据库迁移脚本幂等性验证记录
9. [network-summary.md](network-summary.md): 全流程 HTTP 网络请求监控与异常排查表 (500 异常与预期 403 拦截)
10. [console-summary.md](console-summary.md): 浏览器前端控制台日志监控与阻断性错误排查汇总
@@ -0,0 +1,31 @@
# SYFJGYD学员管理系统 管理端正式账号开通与业务身份绑定验证
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA`
## 一、测试账号与角色档案对应清单
1. `admin` (userId=1): 超级管理员,系统全局最高权限
2. `qa_tea_a` (userId=100): 教师 A,分配 `train_teacher` 角色,绑定师资档案张建国 (`T_DOM_A`)
3. `qa_tea_b` (userId=101): 教师 B,分配 `train_teacher` 角色,绑定师资档案王守诚 (`T_DOM_B`)
4. `qa_stu_a` (userId=102): 学员 A,分配 `train_student` 角色,绑定学员主档李明 (`S_DOM_A`)
5. `qa_stu_b` (userId=103): 学员 B,分配 `train_student` 角色,绑定学员主档赵强 (`S_DOM_B`)
6. `qa_no_id_tea` (userId=104): 未绑定档案教师,分配 `train_teacher` 角色,业务主档未关联
7. `qa_no_id_stu` (userId=105): 未绑定档案学员,分配 `train_student` 角色,业务主档未关联
8. `qa_admin_tea` (userId=106): 复合身份账号,分配 `admin` 与 `train_teacher` 双重角色,绑定师资档案张建国
## 二、功能与安全边界用例验证明细
| 序号 | 验证用例 | 操作说明 | 预期结果 | 实际执行结果 | 判定 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| 1 | **Admin 正向绑定 Teacher** | 管理员调用 `PUT /train/teacher/{tId}/user-binding` 绑定合法教师账号 | 状态码 200,教师档案记录 `user_id` | 成功建立绑定,数据落库 | **PASS** |
| 2 | **Admin 正向绑定 Student** | 管理员调用 `PUT /train/student/{sId}/user-binding` 绑定合法学员账号 | 状态码 200,学员档案记录 `user_id` | 成功建立绑定,数据落库 | **PASS** |
| 3 | **错误角色绑定拒止 (Teacher 错配)** | 将学员角色账号 (`qa_stu_a`) 绑定至教师档案 | 拒绝绑定,返回角色不匹配提示 | 返回 400 校验异常,拒止成功 | **DENIED (PASS)** |
| 4 | **错误角色绑定拒止 (Student 错配)** | 将教师角色账号 (`qa_tea_a`) 绑定至学员档案 | 拒绝绑定,返回角色不匹配提示 | 返回 400 校验异常,拒止成功 | **DENIED (PASS)** |
| 5 | **防静默覆盖 (Silent Overwrite)** | 对已绑定系统用户的教师/学员档案直接发起新绑定请求 | 拒绝静默覆盖,要求必须显式先解绑 | 返回 400 校验错误,防止数据覆盖 | **DENIED (PASS)** |
| 6 | **Teacher XOR Student 业务互斥** | 尝试将已被绑定为教师的 `qa_tea_a` 账号再绑定至学员档案 | 拒绝多重身份冲突,维持身份互斥 | 返回 400 拦截,业务互斥生效 | **PASS** |
| 7 | **解绑不删除系统用户** | 管理员调用 `DELETE /train/teacher/{tId}/user-binding` 解除绑定 | 仅将业务档案 `user_id` 置为 NULL,系统用户账号仍然保留 | 档案解绑成功,`sys_user` 完好保留 | **PASS** |
| 8 | **运行时绑定权限拦截 (Admin)** | 管理员身份调用绑定与解绑接口 | 正常执行 | 具备 2241~2244 权限,允许操作 | **PASS** |
| 9 | **运行时绑定权限拦截 (Teacher)** | 教师账号越权调用 `PUT /train/teacher/{tId}/user-binding` | 权限拦截拒止 | Framework 403 拦截,无权操作 | **DENIED (PASS)** |
| 10 | **运行时绑定权限拦截 (Student)** | 学员账号越权调用 `PUT /train/student/{sId}/user-binding` | 权限拦截拒止 | Framework 403 拦截,无权操作 | **DENIED (PASS)** |
@@ -0,0 +1,15 @@
# SYFJGYD学员管理系统 浏览器前端控制台日志排查汇总
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA`
## 控制台质量指标
- **阻断性控制台错误 (BLOCKING_CONSOLE_ERROR_COUNT)**: `0`
- **未捕获的 JavaScript 异常 (Uncaught Exceptions)**: `0`
- **Vue 渲染运行时崩溃 (Vue Runtime Errors)**: `0`
- **静态资源加载 404 (Asset 404)**: `0`
## 控制台日志审计结论
全链路 21 个核心页面在 Playwright 高清渲染与截图采集过程中,所有 Vue 组件初始化、Element UI 渲染、路由导航守卫均无阻断性错误,页面 Title 统一展示为 `SYFJGYD学员管理系统`,系统运行表现平稳。
@@ -0,0 +1,24 @@
# SYFJGYD学员管理系统 RBAC 横向越权 (IDOR) 与垂直越权测试结果
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA`
- **框架权限拦截语义说明**: 若依 (RuoYi) 框架标准权限校验拦截遵循 `Framework authorization response semantics`,即在网关/MVC 统一拦截时返回 **HTTP 200** 状态码,响应体携带 **`{"code": 403, "msg": "没有权限,请联系管理员授权"}`**。以下测试结果均按实际返回严格记录。
## 越权测试用例执行明细表
| 序号 | 越权类型 | 攻击主体 | 目标资源 / 越权操作 | HTTP Status | Body Code | 拦截响应语义说明 | 最终判定 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 1 | **教师跨域班级 IDOR** | Teacher A (`qa_tea_a`) | 越权查询 Domain B 班级名册 (`PLAN_DOM_B`) | `HTTP 200` | `403` | Framework authorization response semantics (非本人任教计划,数据权限拦截) | **DENIED (PASS)** |
| 2 | **教师跨域课次 IDOR** | Teacher A (`qa_tea_a`) | 越权对 Domain B 课次 (`302001`) 发起考勤录入 | `HTTP 200` | `403` | Framework authorization response semantics (非本人授课课次,禁止打卡) | **DENIED (PASS)** |
| 3 | **教师垂直越权** | Teacher A (`qa_tea_a`) | 越权调用系统用户管理端点 (`GET /system/user/list`) | `HTTP 200` | `403` | Framework authorization response semantics (缺少 `system:user:list` 权限) | **DENIED (PASS)** |
| 4 | **教师垂直越权** | Teacher A (`qa_tea_a`) | 越权调用全局师资档案接口 (`GET /train/teachers`) | `HTTP 200` | `403` | Framework authorization response semantics (缺少全局师资查询权限) | **DENIED (PASS)** |
| 5 | **学员跨人数据 IDOR** | Student A (`qa_stu_a`) | 篡改参数获取 Student B 参训记录 (`?studentId=S_DOM_B`) | `HTTP 200` | `200` | 自服务安全机制强制使用当前 Token 绑定的 `student_id`,篡改参数被强制忽略,仅返回本人数据 | **DENIED (PASS)** |
| 6 | **学员跨人凭据 IDOR** | Student A (`qa_stu_a`) | 越权查询 Student B 报到凭据 (`/train/me/student/checkin-voucher/S_DOM_B`) | `HTTP 200` | `403` | Framework authorization response semantics (非本人凭证,阻断访问) | **DENIED (PASS)** |
| 7 | **学员垂直越权** | Student A (`qa_stu_a`) | 越权调用系统用户管理端点 (`GET /system/user/list`) | `HTTP 200` | `403` | Framework authorization response semantics (缺少系统管理员角色与权限) | **DENIED (PASS)** |
| 8 | **学员垂直越权** | Student A (`qa_stu_a`) | 越权调用全局学员主档接口 (`GET /train/students`) | `HTTP 200` | `403` | Framework authorization response semantics (缺少全局学员管理权限) | **DENIED (PASS)** |
## 越权防御汇总
- **越权数据泄露数 (IDOR_LEAK_COUNT)**: `0`
- **防御结论**: 横向越权 (IDOR) 与垂直越权防御 100% 生效,严格遵循若依权限框架拦截语义规范。
@@ -0,0 +1,27 @@
# SYFJGYD学员管理系统 数据库增量迁移验证记录
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA` (Kingbase 127.0.0.1:4321/training)
- **迁移验证基准执行 (MIGRATION_VALIDATION_RUN)**: `RBAC_E2E_R2_20260916_100800`
- **迁移验证复用标识 (MIGRATION_VALIDATION_REUSED)**: `YES`
- **迁移证据真实性声明 (MIGRATION_EVIDENCE_TRUTHFUL)**: `YES`
## 验证结论与幂等性复核
在基准执行 `RBAC_E2E_R2_20260916_100800` 中,004 与 005 迁移脚本在隔离 QA 数据库中已完成连续两次无报错执行验证:
1. `004_rbac_identity_binding.sql`:
- `004_FIRST_RUN = PASS`
- `004_SECOND_RUN = PASS` (验证 DDL 幂等性,包括 `IF NOT EXISTS`、唯一索引与外键防御)
2. `005_training_roles_permissions.sql`:
- `005_FIRST_RUN = PASS`
- `005_SECOND_RUN = PASS` (验证菜单、角色及权限冲突防护 `ON CONFLICT DO NOTHING` 幂等性)
## 真实性与变更边界审计
本次验收单号 `RBAC_E2E_R2_20260916_102500` 未重新执行 004/005 迁移,严谨真实复用基准执行成果。
原因说明:在 `RBAC_E2E_R2_20260916_100800` 完成后,代码仓库仅发生了一次纯前端系统对外显示名称与文档提交(Commit `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`),`004_rbac_identity_binding.sql`、`005_training_roles_permissions.sql`、数据库表结构及后端 Java 业务代码零变动。
## 生产安全确认
- `004_EXECUTED_PRODUCTION = NO`
- `005_EXECUTED_PRODUCTION = NO`
- `PRODUCTION_DB_MODIFIED = NO`
@@ -0,0 +1,24 @@
# SYFJGYD学员管理系统 HTTP 网络请求监控汇总
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA`
## 核心网络质量指标
- **非预期服务器异常 (UNEXPECTED_HTTP500_COUNT)**: `0`
- **网络中断 / 超时请求数**: `0`
- **网关契约失配数**: `0`
## 预期安全拦截分类统计 (Expected Authorization Rejections)
以下请求均为安全测试用例主动作出的越权渗透或非法参数试探,服务端均按既定安全策略予以受控拒绝,**不属于系统故障**:
1. **教师跨域班级 IDOR 拦截**: 1 次 (返回 code 403,无权限查看)
2. **教师跨域考勤 IDOR 拦截**: 1 次 (返回 code 403,无权限打卡)
3. **教师访问管理员端点拦截**: 2 次 (返回 code 403,权限不足)
4. **学员访问管理员端点拦截**: 2 次 (返回 code 403,权限不足)
5. **学员跨人凭据访问拦截**: 1 次 (返回 code 403,凭据归属不符)
6. **无业务身份自服务拦截**: 2 次 (返回受控 403,提示绑定业务身份)
7. **教师/学员越权调用绑定接口**: 2 次 (返回 code 403,无 2241~2244 权限)
8. **角色错配与业务互斥校验拦截**: 3 次 (返回 code 400,业务规则防御)
全部网络请求与安全防御完全符合契约设计规范。
@@ -0,0 +1,233 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>SYFJGYD学员管理系统 - R.2 三角色认证、功能权限与数据权限 E2E 验收报告</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 30px; background: #f5f7fa; color: #2c3e50; line-height: 1.6; }
.card { background: #fff; border-radius: 8px; padding: 25px; box-shadow: 0 2px 12px 0 rgba(0,0,0,0.08); margin-bottom: 25px; }
h1, h2, h3 { color: #1f2f3d; }
.badge-pass { display: inline-block; padding: 4px 12px; border-radius: 4px; font-weight: bold; background: #67c23a; color: #fff; }
table { width: 100%; border-collapse: collapse; margin-top: 15px; margin-bottom: 15px; }
th, td { border: 1px solid #ebeef5; padding: 10px 15px; text-align: left; font-size: 14px; }
th { background: #fafafa; font-weight: bold; }
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 20px; margin-top: 15px; }
.gallery-item { border: 1px solid #ebeef5; border-radius: 6px; padding: 12px; background: #fafafa; }
.gallery-title { font-weight: bold; font-size: 14px; margin-bottom: 8px; color: #303133; }
.gallery-item img { width: 100%; height: auto; border-radius: 4px; border: 1px solid #dcdfe6; transition: transform 0.2s; }
.gallery-item img:hover { transform: scale(1.02); }
.gallery-desc { font-size: 12px; color: #909399; margin-top: 6px; }
code { background: #f0f2f5; padding: 2px 6px; border-radius: 4px; font-family: monospace; }
</style>
</head>
<body>
<div class="card">
<h1>SYFJGYD学员管理系统 · R.2 三角色认证、功能权限与数据权限 E2E 验收交付报告</h1>
<p>
<b>验收单号 (RUN_ID):</b> <code>RBAC_E2E_R2_20260916_102500</code> &nbsp;|&nbsp;
<b>代码候选基线:</b> <code>81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3</code> &nbsp;|&nbsp;
<b>环境:</b> <code>ISOLATED QA</code> &nbsp;|&nbsp;
<span class="badge-pass">E2E_R2_STATUS: PASS</span>
</p>
<p>
<b>测试数据库:</b> Kingbase 隔离 QA 环境 (127.0.0.1:4321/training) &nbsp;|&nbsp;
<b>迁移复用基准:</b> <code>RBAC_E2E_R2_20260916_100800</code> (004/005 双次幂等通过) &nbsp;|&nbsp;
<b>全链路无损截图:</b> 21 张就绪
</p>
</div>
<div class="card">
<h2>核心检查项与断言结论</h2>
<table>
<tr><th>分类</th><th>测试项</th><th>断言结论</th><th>说明</th></tr>
<tr><td>环境隔离</td><td>生产数据库与服务隔离保护</td><td>PASS (TEST_DB_ISOLATED=YES)</td><td>生产库 192.168.139.230 与生产端口 8082 零触碰</td></tr>
<tr><td>迁移验证</td><td>004/005 增量迁移脚本幂等性</td><td>PASS (MIGRATION_VALIDATION_REUSED=YES)</td><td>复用基准运行成果,004/005 连续执行两次无报错</td></tr>
<tr><td>账号开通</td><td>管理端正式开户与角色分配</td><td>PASS (ADMIN_CAN_CREATE_SYS_USER=YES)</td><td>开通 7 个专用测试账号,精准分配业务角色</td></tr>
<tr><td>账号绑定</td><td>管理端业务档案与用户绑定</td><td>PASS (ADMIN_ACCOUNT_BINDING=PASS)</td><td>正向绑定建立正常,解绑不删除系统用户</td></tr>
<tr><td>安全边界</td><td>Teacher XOR Student 互斥保护</td><td>PASS (TEACHER_STUDENT_MUTEX_RESULT=PASS)</td><td>防静默覆盖、角色错配拒止、业务互斥拦截</td></tr>
<tr><td>越权防护</td><td>IDOR 横向与垂直越权渗透防御</td><td>PASS (IDOR_LEAK_COUNT=0)</td><td>严格遵循若依框架权限拦截语义,越权请求 100% 阻断</td></tr>
<tr><td>教师工作台</td><td>Own Data 数据范围精确控制</td><td>PASS (TEACHER_OWN_DATA=PASS)</td><td>仅见本人任教计划、课表、学员及考勤打卡</td></tr>
<tr><td>学员中心</td><td>学员业务闭环与数据安全</td><td>PASS (STUDENT_OWN_DATA=PASS)</td><td>仅见本人报到凭证、参训记录、课表及画像</td></tr>
<tr><td>特例主体</td><td>未绑定账号与复合角色支持</td><td>PASS</td><td>无身份账号受控提示,管理员+教师双角色优先驾驶舱</td></tr>
<tr><td>管理回归</td><td>核心业务 13 大模块综合回归</td><td>PASS (ADMIN_REGRESSION=PASS)</td><td>排课预检确认、报到 0->1、考勤幂等性全部正常</td></tr>
</table>
</div>
<div class="card">
<h2>全链路 21 张无损高清截图画廊 (SCREENSHOT_COUNT = 21)</h2>
<div class="gallery-grid">
<div class="gallery-item">
<div class="gallery-title">01. 管理员首页与系统看板</div>
<a href="screenshots/01-admin-home.png" target="_blank">
<img src="screenshots/01-admin-home.png" alt="01. 管理员首页与系统看板" loading="lazy" />
</a>
<div class="gallery-desc">管理员登录后首页及统一系统显示名称 (<code>screenshots/01-admin-home.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">02. 管理端教师账号绑定</div>
<a href="screenshots/02-admin-teacher-bind.png" target="_blank">
<img src="screenshots/02-admin-teacher-bind.png" alt="02. 管理端教师账号绑定" loading="lazy" />
</a>
<div class="gallery-desc">管理员为教师业务档案绑定系统用户 (<code>screenshots/02-admin-teacher-bind.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">03. 管理端学员账号绑定</div>
<a href="screenshots/03-admin-student-bind.png" target="_blank">
<img src="screenshots/03-admin-student-bind.png" alt="03. 管理端学员账号绑定" loading="lazy" />
</a>
<div class="gallery-desc">管理员为学员业务档案绑定系统用户 (<code>screenshots/03-admin-student-bind.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">04. 教师 A 工作台看板</div>
<a href="screenshots/04-teacher-a-dashboard.png" target="_blank">
<img src="screenshots/04-teacher-a-dashboard.png" alt="04. 教师 A 工作台看板" loading="lazy" />
</a>
<div class="gallery-desc">任课教师 A 登录后工作台首页 (<code>screenshots/04-teacher-a-dashboard.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">05. 教师 A 本人任教计划</div>
<a href="screenshots/05-teacher-a-plans.png" target="_blank">
<img src="screenshots/05-teacher-a-plans.png" alt="05. 教师 A 本人任教计划" loading="lazy" />
</a>
<div class="gallery-desc">教师 A 仅能查看本人所属授课计划 (<code>screenshots/05-teacher-a-plans.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">06. 教师 A 本人授课课表</div>
<a href="screenshots/06-teacher-a-timetable.png" target="_blank">
<img src="screenshots/06-teacher-a-timetable.png" alt="06. 教师 A 本人授课课表" loading="lazy" />
</a>
<div class="gallery-desc">教师 A 仅能查看本人所授课次安排 (<code>screenshots/06-teacher-a-timetable.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">07. 教师 A 本人班级学员</div>
<a href="screenshots/07-teacher-a-students.png" target="_blank">
<img src="screenshots/07-teacher-a-students.png" alt="07. 教师 A 本人班级学员" loading="lazy" />
</a>
<div class="gallery-desc">教师 A 仅能查看本人任教班级花名册 (<code>screenshots/07-teacher-a-students.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">08. 教师跨班 IDOR 拦截</div>
<a href="screenshots/08-teacher-cross-plan-denied.png" target="_blank">
<img src="screenshots/08-teacher-cross-plan-denied.png" alt="08. 教师跨班 IDOR 拦截" loading="lazy" />
</a>
<div class="gallery-desc">教师 A 跨域访问 Domain B 班级被受控拦截 (<code>screenshots/08-teacher-cross-plan-denied.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">09. 教师跨课次考勤拦截</div>
<a href="screenshots/09-teacher-cross-lesson-denied.png" target="_blank">
<img src="screenshots/09-teacher-cross-lesson-denied.png" alt="09. 教师跨课次考勤拦截" loading="lazy" />
</a>
<div class="gallery-desc">教师 A 跨域录入 Domain B 课次考勤被受控拦截 (<code>screenshots/09-teacher-cross-lesson-denied.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">10. 教师 B 域数据对称验证</div>
<a href="screenshots/10-teacher-b-domain.png" target="_blank">
<img src="screenshots/10-teacher-b-domain.png" alt="10. 教师 B 域数据对称验证" loading="lazy" />
</a>
<div class="gallery-desc">教师 B 仅可查看 Domain B 对应授课数据 (<code>screenshots/10-teacher-b-domain.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">11. 学员 A 个人培训中心</div>
<a href="screenshots/11-student-a-dashboard.png" target="_blank">
<img src="screenshots/11-student-a-dashboard.png" alt="11. 学员 A 个人培训中心" loading="lazy" />
</a>
<div class="gallery-desc">参训学员 A 登录后个人中心首页 (<code>screenshots/11-student-a-dashboard.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">12. 学员 A 本人参训记录</div>
<a href="screenshots/12-student-a-enrollments.png" target="_blank">
<img src="screenshots/12-student-a-enrollments.png" alt="12. 学员 A 本人参训记录" loading="lazy" />
</a>
<div class="gallery-desc">学员 A 仅能查看本人参训报名档案 (<code>screenshots/12-student-a-enrollments.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">13. 学员 A 本人个人课表</div>
<a href="screenshots/13-student-a-timetable.png" target="_blank">
<img src="screenshots/13-student-a-timetable.png" alt="13. 学员 A 本人个人课表" loading="lazy" />
</a>
<div class="gallery-desc">学员 A 仅能查看本人参训排课课表 (<code>screenshots/13-student-a-timetable.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">14. 学员 A 本人报到凭证</div>
<a href="screenshots/14-student-a-report.png" target="_blank">
<img src="screenshots/14-student-a-report.png" alt="14. 学员 A 本人报到凭证" loading="lazy" />
</a>
<div class="gallery-desc">学员 A 仅能查看本人报到凭据与状态 (<code>screenshots/14-student-a-report.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">15. 学员 A 本人打卡明细</div>
<a href="screenshots/15-student-a-attendance.png" target="_blank">
<img src="screenshots/15-student-a-attendance.png" alt="15. 学员 A 本人打卡明细" loading="lazy" />
</a>
<div class="gallery-desc">学员 A 仅能查看本人考勤打卡流水与画像 (<code>screenshots/15-student-a-attendance.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">16. 学员越权管理接口拦截</div>
<a href="screenshots/16-student-admin-api-denied.png" target="_blank">
<img src="screenshots/16-student-admin-api-denied.png" alt="16. 学员越权管理接口拦截" loading="lazy" />
</a>
<div class="gallery-desc">学员 A 越权调用管理端接口被受控拦截 (<code>screenshots/16-student-admin-api-denied.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">17. 学员 B 域数据对称验证</div>
<a href="screenshots/17-student-b-domain.png" target="_blank">
<img src="screenshots/17-student-b-domain.png" alt="17. 学员 B 域数据对称验证" loading="lazy" />
</a>
<div class="gallery-desc">学员 B 仅能查看 Domain B 本人数据 (<code>screenshots/17-student-b-domain.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">18. 无业务身份账号受控提示</div>
<a href="screenshots/18-no-identity-denied.png" target="_blank">
<img src="screenshots/18-no-identity-denied.png" alt="18. 无业务身份账号受控提示" loading="lazy" />
</a>
<div class="gallery-desc">未绑定业务主档账号访问自服务返回友好拒止 (<code>screenshots/18-no-identity-denied.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">19. 管理员+教师复合身份</div>
<a href="screenshots/19-admin-teacher-landing.png" target="_blank">
<img src="screenshots/19-admin-teacher-landing.png" alt="19. 管理员+教师复合身份" loading="lazy" />
</a>
<div class="gallery-desc">复合角色默认进入管理端并可使用工作台 (<code>screenshots/19-admin-teacher-landing.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">20. 教师学员业务互斥校验</div>
<a href="screenshots/20-teacher-student-mutex.png" target="_blank">
<img src="screenshots/20-teacher-student-mutex.png" alt="20. 教师学员业务互斥校验" loading="lazy" />
</a>
<div class="gallery-desc">同一账号禁止同时绑定教师和学员 (<code>screenshots/20-teacher-student-mutex.png</code>)</div>
</div>
<div class="gallery-item">
<div class="gallery-title">21. 管理端核心业务综合回归</div>
<a href="screenshots/21-admin-regression.png" target="_blank">
<img src="screenshots/21-admin-regression.png" alt="21. 管理端核心业务综合回归" loading="lazy" />
</a>
<div class="gallery-desc">排课预检确认、报到、考勤等核心流程回归 (<code>screenshots/21-admin-regression.png</code>)</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,46 @@
# R.2 三角色认证、功能权限与数据权限 E2E 综合验收报告
## 一、验收运行总览
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA` (数据库 127.0.0.1:4321, Redis 127.0.0.1:6379, 后端 8090, 前端 8080)
- **后端单测与契约测试**: `BACKEND_TEST_TOTAL = 205`,0 failures, 0 errors
- **构建状态**: Maven `BUILD SUCCESS`,Frontend `BUILD SUCCESS`,ESLint 0 errors / 0 warnings
- **生产隔离安全**: `004_EXECUTED_PRODUCTION = NO`, `005_EXECUTED_PRODUCTION = NO`, `PRODUCTION_DB_MODIFIED = NO`
- **综合验收结论**: `E2E_R2_STATUS = PASS`
## 二、核心验证阶段与断言矩阵
1. **增量迁移验证 (复用)**: 复用基准 `RBAC_E2E_R2_20260916_100800` 成果,004 与 005 连续两次幂等执行全部 PASS。
2. **账号开通与角色分配**: 管理员调用 `POST /system/user` 成功创建 7 个独立测试账号并分配合规角色。
3. **账号绑定与安全边界**: 教师/学员绑定成功;角色错配拒止;防静默覆盖生效;Teacher XOR Student 互斥成立;解绑操作不删除系统用户;Teacher/Student 越权调用绑定接口被 403 拦截。
4. **三角色登录与菜单隔离**: Admin、Teacher A/B、Student A/B 登录与 Token 发放完全正常。Teacher/Student 动态路由完全隔离,无任何管理端与全局档案菜单。
5. **教师自服务与 IDOR 防御**: 仅可见本人所授课程与班级;跨班名册 IDOR 拦截 (HTTP 200 / code 403);跨课次考勤打卡拦截 (HTTP 200 / code 403);调用 Admin API 被 403 拦截;Teacher B 对称验证 PASS。
6. **学员自服务与 IDOR 防御**: 仅可见本人参训、排课日程、报到凭证、考勤打卡与学情画像;参数篡改防御生效;跨人凭证拦截 (HTTP 200 / code 403);调用 Admin API 被 403 拦截;Student B 对称验证 PASS。
7. **特例主体处理**: 无业务身份账号访问自服务返回受控友好 403 提示;Admin + Teacher 复合角色默认优先进入管理驾驶舱,工作台自服务双向连通。
## 三、Admin 核心业务 13 大模块全覆盖回归核实
| 序号 | 业务模块 | 覆盖功能点 | 自动化回归结论 |
| :--- | :--- | :--- | :--- |
| 1 | **Teacher (师资管理)** | 教师档案列表、详情展示、业务字段检索 | **PASS** |
| 2 | **Student (学员主档)** | 学员花名册列表、身份类别过滤、分页查询 | **PASS** |
| 3 | **Course (课程库)** | 课程分类字典、课程目录检索、学时学分配置 | **PASS** |
| 4 | **Plan (培训计划)** | 开班计划制定、培训周期管理、班级状态流转 | **PASS** |
| 5 | **Enrollment (参训名册)** | 学员参训名册关联、报名状态流转与名单核对 | **PASS** |
| 6 | **Scheduling (排课与日程)** | 课次编排、时间场地校验、**软冲突预检与二次确认保存** | **PASS** |
| 7 | **Report (学员报到)** | 报到名单流水、**报到状态 0->1 确认核销** | **PASS** |
| 8 | **Attendance (考勤打卡)** | 考勤流水记录、**签到签退幂等性验证、非法状态 5 拒止** | **PASS** |
| 9 | **Board (班牌信息展示)** | 培训场地动态看板、今日日程与班级动态大屏 | **PASS** |
| 10 | **Analytics (学情分析)** | 参训率、考勤合格率、综合学情统计分析大屏 | **PASS** |
| 11 | **Account Binding (账号绑定)** | 教师与学员正式用户绑定弹窗、解除绑定功能 | **PASS** |
| 12 | **OperLog (操作日志审计)** | 审计流水全量记录、管理端关键操作日志落库正常 | **PASS** |
| 13 | **Swagger (API 契约文档)** | API 契约接口文档规范渲染与可访问性验证 | **PASS** |
## 四、网络与控制台稳定性指标
- **非预期 HTTP 500 数量 (UNEXPECTED_HTTP500_COUNT)**: `0`
- **阻断性控制台错误 (BLOCKING_CONSOLE_ERROR_COUNT)**: `0`
- **受控拦截 (Expected Authorization Rejections)**: 越权与互斥拦截均符合框架标准语义,未发生系统未捕获异常。
## 五、验收判定
**E2E_R2_STATUS = PASS**
@@ -0,0 +1,27 @@
# SYFJGYD学员管理系统 三角色及特例主体权限矩阵
- **验收单号 (RUN_ID)**: `RBAC_E2E_R2_20260916_102500`
- **代码候选基准 (CANDIDATE_COMMIT)**: `81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3`
- **系统对外正式名称 (SYSTEM_DISPLAY_NAME)**: `SYFJGYD学员管理系统`
- **测试环境 (ENVIRONMENT)**: `ISOLATED QA`
## 覆盖主体全量清单
1. **Admin** (`admin`): 系统与教务超级管理员
2. **Teacher A** (`qa_tea_a`): 域 A 教师 (张建国 `T_DOM_A`)
3. **Teacher B** (`qa_tea_b`): 域 B 教师 (王守诚 `T_DOM_B`)
4. **Student A** (`qa_stu_a`): 域 A 学员 (李明 `S_DOM_A`)
5. **Student B** (`qa_stu_b`): 域 B 学员 (赵强 `S_DOM_B`)
6. **NoIdentity Teacher/Student** (`qa_no_id_tea`, `qa_no_id_stu`): 拥有教师/学员角色但未绑定业务主档账号
7. **Admin + Teacher** (`qa_admin_tea`): 同时拥有管理员与教师双重角色主体
## 详细维度对比矩阵
| 序号 | 验证主体 | 用户账号 | 角色分配 | Menu (动态路由可见性) | Self-Service (自服务Own Data) | Admin API (全局管理接口) | Binding API (2241~2244 账号绑定) |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 1 | **Admin** | `admin` | `admin` | 全局系统管理、教务全业务、排课、考勤、报到、分析等 | 全局可见,不限制单人 | 完全允许 (全量读写放行) | 允许 (具备 2241~2244 完整权限) |
| 2 | **Teacher A** | `qa_tea_a` | `train_teacher` | 仅教师专属工作台 (`/train/me/teacher`);无任何系统管理菜单 | 仅限本人任教课程、排课计划、课表、学员及考勤打卡 | 拒绝 (Framework 403 拦截) | 拒绝 (403 拦截,无 2241~2244 权限) |
| 3 | **Teacher B** | `qa_tea_b` | `train_teacher` | 仅教师专属工作台 (`/train/me/teacher`);无任何系统管理菜单 | 仅限本人任教域 B 对应班级、课表及学员考勤 | 拒绝 (Framework 403 拦截) | 拒绝 (403 拦截,无 2241~2244 权限) |
| 4 | **Student A** | `qa_stu_a` | `train_student` | 仅学员个人培训中心 (`/train/me/student`);无任何系统管理菜单 | 仅限本人报到凭证、参训记录、个人课表、考勤明细及画像 | 拒绝 (Framework 403 拦截) | 拒绝 (403 拦截,无 2241~2244 权限) |
| 5 | **Student B** | `qa_stu_b` | `train_student` | 仅学员个人培训中心 (`/train/me/student`);无任何系统管理菜单 | 仅限本人域 B 报到凭证、参训记录、个人课表及画像 | 拒绝 (Framework 403 拦截) | 拒绝 (403 拦截,无 2241~2244 权限) |
| 6 | **NoIdentity (Tea/Stu)** | `qa_no_id_tea` / `qa_no_id_stu` | `train_teacher` / `train_student` | 仅工作台/学员中心框架路由 | 受控拒止 (返回友好未绑定提示,严禁越权返回任意数据) | 拒绝 (Framework 403 拦截) | 拒绝 (403 拦截,无 2241~2244 权限) |
| 7 | **Admin + Teacher** | `qa_admin_tea` | `admin` + `train_teacher` | 默认优先 Landing 系统管理与教务驾驶舱,工作台可无缝切换 | 管理端全局与教师自服务接口双向连通正常 | 完全允许 (管理员权限优先放行) | 允许 (具备 2241~2244 完整权限) |
@@ -0,0 +1,53 @@
{
"RUN_ID": "RBAC_E2E_R2_20260916_102500",
"CANDIDATE_COMMIT": "81e7d5447c2ea32e4a6c717cd3bb3d1e38b6aad3",
"SYSTEM_DISPLAY_NAME": "SYFJGYD学员管理系统",
"ENVIRONMENT": "ISOLATED QA",
"TEST_DB_ISOLATED": "YES",
"MIGRATION_VALIDATION_RUN": "RBAC_E2E_R2_20260916_100800",
"MIGRATION_VALIDATION_REUSED": "YES",
"MIGRATION_EVIDENCE_TRUTHFUL": "YES",
"ADMIN_CAN_CREATE_SYS_USER": "YES",
"ADMIN_CAN_ASSIGN_ROLE": "YES",
"ADMIN_CAN_BIND_TEACHER": "YES",
"ADMIN_CAN_BIND_STUDENT": "YES",
"ADMIN_LOGIN": "PASS",
"ADMIN_ACCOUNT_BINDING": "PASS",
"TEACHER_A_LOGIN": "PASS",
"TEACHER_B_LOGIN": "PASS",
"STUDENT_A_LOGIN": "PASS",
"STUDENT_B_LOGIN": "PASS",
"TEACHER_MENU_ISOLATION": "PASS",
"STUDENT_MENU_ISOLATION": "PASS",
"TEACHER_OWN_DATA": "PASS",
"TEACHER_CROSS_PLAN_IDOR": "DENIED (PASS)",
"TEACHER_CROSS_LESSON_IDOR": "DENIED (PASS)",
"TEACHER_ADMIN_API_DENIED": "DENIED (PASS)",
"STUDENT_OWN_DATA": "PASS",
"STUDENT_CROSS_DATA_IDOR": "DENIED (PASS)",
"STUDENT_ADMIN_API_DENIED": "DENIED (PASS)",
"NO_IDENTITY_RESULT": "DENIED (PASS)",
"ADMIN_PLUS_TEACHER_RESULT": "PASS",
"TEACHER_STUDENT_MUTEX_RESULT": "PASS",
"BIND_PERMISSION_RUNTIME_RESULT": "PASS",
"ADMIN_REGRESSION": "PASS",
"ROLE_MATRIX_COMPLETE": "YES",
"IDOR_EVIDENCE_COMPLETE": "YES",
"ACCOUNT_BINDING_EVIDENCE_COMPLETE": "YES",
"UNEXPECTED_HTTP500_COUNT": 0,
"BLOCKING_CONSOLE_ERROR_COUNT": 0,
"SCREENSHOT_COUNT": 21,
"EVIDENCE_DOCUMENT_COUNT": 10,
"EVIDENCE_PATH": "/Users/wzb/Desktop/项目/RailwaySystem-worktrees/rbac-r2/docs/reports/e2e-r2/RBAC_E2E_R2_20260916_102500",
"BACKEND_TEST_TOTAL": 205,
"MAVEN_PACKAGE": "BUILD SUCCESS",
"FRONTEND_BUILD": "BUILD SUCCESS",
"ESLINT_ERRORS": 0,
"004_EXECUTED_PRODUCTION": "NO",
"005_EXECUTED_PRODUCTION": "NO",
"PRODUCTION_DB_MODIFIED": "NO",
"E2E_R2_STATUS": "PASS",
"COMMIT": "NO",
"PUSH": "NO",
"DEPLOY": "NO"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB