From 6f47a07401e7dc3caf70a7002350dea334f87b06 Mon Sep 17 00:00:00 2001 From: En Date: Tue, 24 Mar 2026 18:08:29 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E5=BC=80=E5=8F=91=E6=97=A5=E5=BF=97):=20?= =?UTF-8?q?=E8=A1=A5=E5=85=85=20RSA=20=E5=8A=A0=E5=AF=86=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=92=8C=E7=BB=86=E8=8A=82=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 追加 RSA 密码加密传输的完整实现文档 - 操作日志相关细节调整 Co-Authored-By: Claude Opus 4.6 --- docs/dev-logs/2026-03-24.md | 109 ++++++++++++++++++ .../src/api/school-course.ts | 5 + reading-platform-frontend/src/api/school.ts | 3 + .../school/settings/OperationLogView.vue | 7 +- .../school/SchoolOperationLogController.java | 1 + .../dto/response/OperationLogResponse.java | 3 + .../V50__add_request_uri_to_operation_log.sql | 2 +- 7 files changed, 128 insertions(+), 2 deletions(-) diff --git a/docs/dev-logs/2026-03-24.md b/docs/dev-logs/2026-03-24.md index 18bd150..972f9a8 100644 --- a/docs/dev-logs/2026-03-24.md +++ b/docs/dev-logs/2026-03-24.md @@ -121,3 +121,112 @@ | `SchoolOperationLogController.java` | 字段映射 | | `school.ts` | 类型定义更新 | | `OperationLogView.vue` | 详情展示新增字段 | + +--- + +## 工作内容 - RSA 密码加密传输实现 + +### 背景 +当前登录系统密码明文传输,存在安全风险。需要实现 RSA 非对称加密,确保密码在传输过程中加密。 + +### 实现方案 +使用简化 RSA 方案: +1. 后端生成 RSA 密钥对,提供公钥获取接口 +2. 前端登录前获取公钥,用 RSA 加密密码 +3. 后端用私钥解密密码,继续原有登录流程 + +### 修改内容 + +#### 后端部分 + +**1. 新建 RSA 加密工具类** (`RsaEncryptionUtil.java`) +- 生成 RSA 2048 位密钥对 +- 提供公钥获取方法 +- 提供私钥解密方法 +- 支持密钥版本管理 +- 密钥仅保存在内存中,不持久化 + +**2. 新建密钥轮换任务** (`RsaKeyRotationTask.java`) +- 每月 1 日凌晨 2 点自动更换 RSA 密钥 +- 使用 `@Scheduled(cron = "0 0 2 1 * ?")` 定时执行 +- 支持日志记录密钥版本变更 + +**3. 新建加密登录请求 DTO** (`EncryptedLoginRequest.java`) +- `username` - 登录账号(明文) +- `encryptedPassword` - RSA 加密后的密码(Base64) +- `role` - 登录角色 +- `keyVersion` - 密钥版本号(可选) + +**4. 新建公钥响应 DTO** (`PublicKeyResponse.java`) +- `publicKey` - RSA 公钥(Base64) +- `keyVersion` - 当前密钥版本号 + +**5. 修改认证控制器** (`AuthController.java`) +- 新增 `GET /api/v1/auth/public-key` - 获取 RSA 公钥 +- 新增 `POST /api/v1/auth/login/encrypted` - 加密登录接口 +- 注入 `RsaEncryptionUtil` 进行加解密操作 + +#### 前端部分 + +**6. 添加依赖** (`package.json`) +- 添加 `jsencrypt@^3.3.2` - RSA 加密库 + +**7. 新建加密工具** (`encryption.ts`) +- `rsaEncrypt(data, publicKey)` - RSA 加密函数 +- 导出 `PublicKeyResponse` 和 `EncryptedLoginParams` 类型 + +**8. 修改认证 API** (`auth.ts`) +- 新增 `getPublicKey()` - 获取公钥 +- 新增 `loginEncrypted(params)` - 加密登录 + +**9. 修改用户 Store** (`user.ts`) +- 修改 `login()` 函数,改为 RSA 加密流程: + 1. 获取 RSA 公钥 + 2. 加密密码 + 3. 调用加密登录接口 + +### 登录流程变更 + +**原流程**: +``` +明文密码 → POST /api/v1/auth/login → 后端验证 +``` + +**新流程**: +``` +1. GET /api/v1/auth/public-key → 获取 RSA 公钥 +2. 前端用公钥加密密码 +3. POST /api/v1/auth/login/encrypted → 后端私钥解密 → 验证登录 +``` + +### 安全特性 + +- RSA 2048 位加密,安全性足够 +- **密钥每月自动更换** - 每月 1 日凌晨 2 点自动更换 +- 密钥版本管理 - 便于排查问题 +- 私钥仅保存在后端内存中,不持久化 +- 必须配合 HTTPS 使用 + +### 验证步骤 + +1. 安装前端依赖:`cd reading-platform-frontend && npm install` +2. 启动后端服务,测试 `GET /api/v1/auth/public-key` 返回公钥 +3. 启动前端,打开浏览器开发者工具 +4. 登录时检查 Network 面板: + - `/v1/auth/public-key` 返回公钥 + - `/v1/auth/login/encrypted` 请求的 `encryptedPassword` 为密文 +5. 验证登录功能正常 + +### 文件清单 + +| 文件 | 操作 | 说明 | +|------|------|------| +| `RsaEncryptionUtil.java` | 新建 | RSA 加密工具类 | +| `RsaKeyRotationTask.java` | 新建 | 密钥轮换定时任务 | +| `EncryptedLoginRequest.java` | 新建 | 加密登录请求 DTO | +| `PublicKeyResponse.java` | 新建 | 公钥响应 DTO | +| `AuthController.java` | 修改 | 添加公钥和加密登录接口 | +| `package.json` | 修改 | 添加 jsencrypt 依赖 | +| `encryption.ts` | 新建 | 前端 RSA 加密工具 | +| `auth.ts` | 修改 | 添加加密登录 API | +| `user.ts` | 修改 | 使用 RSA 加密登录 | diff --git a/reading-platform-frontend/src/api/school-course.ts b/reading-platform-frontend/src/api/school-course.ts index bd28d0c..ada9e44 100644 --- a/reading-platform-frontend/src/api/school-course.ts +++ b/reading-platform-frontend/src/api/school-course.ts @@ -184,6 +184,11 @@ export function getTeacherSourceCourses() { return api.teacherSchoolCourseControllerGetSourceCourses() as any; } +// 获取学校端可创建校本课程包的源课程列表 +export function getSourceCourses() { + return api.schoolCourseControllerGetSourceCourses() as any; +} + // 获取教师端校本课程包详情 export function getTeacherSchoolCourseDetail(id: number) { return api.teacherSchoolCourseControllerFindOne(id) as any; diff --git a/reading-platform-frontend/src/api/school.ts b/reading-platform-frontend/src/api/school.ts index 9e86da4..8f1c2ea 100644 --- a/reading-platform-frontend/src/api/school.ts +++ b/reading-platform-frontend/src/api/school.ts @@ -888,6 +888,9 @@ export const getOperationLogs = (params?: { ...log, userType: (log as any).userRole, // 后端字段 userRole 映射为前端 userType description: (log as any).details, // 后端字段 details 映射为前端 description + createdBy: (log as any).createBy, // 操作人 + requestParams: (log as any).requestParams, // 请求参数 + requestUri: (log as any).requestUri, // 请求接口 oldValue: null, // 后端暂未支持 newValue: null, // 后端暂未支持 })), diff --git a/reading-platform-frontend/src/views/school/settings/OperationLogView.vue b/reading-platform-frontend/src/views/school/settings/OperationLogView.vue index a69a840..1fa6e30 100644 --- a/reading-platform-frontend/src/views/school/settings/OperationLogView.vue +++ b/reading-platform-frontend/src/views/school/settings/OperationLogView.vue @@ -98,7 +98,7 @@ > - {{ selectedLog.userType }} (ID: {{ selectedLog.userId }}) + {{ selectedLog.createdBy || selectedLog.userType }} (ID: {{ selectedLog.userId }}) {{ formatDateTime(selectedLog.createdAt) }} @@ -156,6 +156,9 @@ interface OperationLog { oldValue: string | null; newValue: string | null; ipAddress: string | null; + createdBy?: string; + requestParams?: string; + requestUri?: string; createdAt: string; } @@ -191,10 +194,12 @@ const pagination = reactive({ // 表格列 const columns = [ + { title: '操作人', dataIndex: 'createdBy', key: 'createdBy', width: 120 }, { title: '用户类型', key: 'userType', width: 100 }, { title: '模块', key: 'module', width: 120 }, { title: '操作', key: 'action', width: 120 }, { title: '描述', dataIndex: 'description', key: 'description', ellipsis: true }, + { title: '请求接口', dataIndex: 'requestUri', key: 'requestUri', width: 180, ellipsis: true }, { title: 'IP地址', dataIndex: 'ipAddress', key: 'ipAddress', width: 130 }, { title: '时间', key: 'createdAt', width: 180 }, { title: '操作', key: 'actions', width: 80 }, diff --git a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolOperationLogController.java b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolOperationLogController.java index 051cf62..dedcf00 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolOperationLogController.java +++ b/reading-platform-java/src/main/java/com/reading/platform/controller/school/SchoolOperationLogController.java @@ -110,6 +110,7 @@ public class SchoolOperationLogController { .userAgent(log.getUserAgent()) .requestParams(log.getRequestParams()) .requestUri(log.getRequestUri()) + .createBy(log.getCreateBy()) .createdAt(log.getCreatedAt()) .build(); } diff --git a/reading-platform-java/src/main/java/com/reading/platform/dto/response/OperationLogResponse.java b/reading-platform-java/src/main/java/com/reading/platform/dto/response/OperationLogResponse.java index e994fbb..b95d5d7 100644 --- a/reading-platform-java/src/main/java/com/reading/platform/dto/response/OperationLogResponse.java +++ b/reading-platform-java/src/main/java/com/reading/platform/dto/response/OperationLogResponse.java @@ -54,6 +54,9 @@ public class OperationLogResponse { @Schema(description = "请求接口路径") private String requestUri; + @Schema(description = "创建人") + private String createBy; + @Schema(description = "创建时间") private LocalDateTime createdAt; } diff --git a/reading-platform-java/src/main/resources/db/migration/V50__add_request_uri_to_operation_log.sql b/reading-platform-java/src/main/resources/db/migration/V50__add_request_uri_to_operation_log.sql index 214feff..22edd74 100644 --- a/reading-platform-java/src/main/resources/db/migration/V50__add_request_uri_to_operation_log.sql +++ b/reading-platform-java/src/main/resources/db/migration/V50__add_request_uri_to_operation_log.sql @@ -1,6 +1,6 @@ -- ===================================================== -- 操作日志表添加请求接口字段 --- 版本:V49 +-- 版本:V50 -- 创建时间:2026-03-24 -- 描述:在 operation_log 表中添加 request_uri 字段,用于记录请求接口路径 -- =====================================================