feat(login, zulip): 引入 JWT 验证并重构 API 密钥管理

### 详细变更描述

* **修复 JWT 签名冲突**:重构 `LoginService.generateTokenPair()`,移除载荷(Payload)中的 `iss` (issuer) 与 `aud` (audience) 字段,解决签名校验失败的问题。
* **统一验证逻辑**:更新 `ZulipService` 以调用 `LoginService.verifyToken()`,消除重复的 JWT 校验代码,确保逻辑单一职责化(Single Responsibility)。
* **修复硬编码 API 密钥问题**:消息发送功能不再依赖静态配置,改为从 Redis 动态读取用户真实的 API 密钥。
* **解耦依赖注入**:在 `ZulipModule` 中注入 `AuthModule` 依赖,以支持标准的 Token 验证流程。
* **完善技术文档**:补充了关于 JWT 验证流程及 API 密钥管理逻辑的详细文档。
* **新增测试工具**:添加 `test-get-messages.js` 脚本,用于验证通过 WebSocket 接收消息的功能。
* **更新自动化脚本**:同步更新了 API 密钥验证及用户注册校验的快速测试脚本。
* **端到端功能验证**:确保消息发送逻辑能够正确映射并调用用户真实的 Zulip API 密钥。
This commit is contained in:
angjustinl
2026-01-06 18:51:37 +08:00
parent 3733717d1f
commit 8f9a6e7f9d
8 changed files with 763 additions and 187 deletions

View File

@@ -663,35 +663,34 @@ export class LoginService {
throw new Error('JWT_SECRET未配置');
}
// 1. 创建访问令牌载荷
const accessPayload: JwtPayload = {
// 1. 创建访问令牌载荷不包含iss和aud这些通过options传递
const accessPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'> = {
sub: user.id.toString(),
username: user.username,
role: user.role,
email: user.email,
type: 'access',
iat: currentTime,
iss: 'whale-town',
aud: 'whale-town-users',
};
// 2. 创建刷新令牌载荷(有效期更长)
const refreshPayload: JwtPayload = {
const refreshPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'> = {
sub: user.id.toString(),
username: user.username,
role: user.role,
type: 'refresh',
iat: currentTime,
iss: 'whale-town',
aud: 'whale-town-users',
};
// 3. 生成访问令牌使用NestJS JwtService
const accessToken = await this.jwtService.signAsync(accessPayload);
// 3. 生成访问令牌使用NestJS JwtService通过options传递iss和aud
const accessToken = await this.jwtService.signAsync(accessPayload, {
issuer: 'whale-town',
audience: 'whale-town-users',
});
// 4. 生成刷新令牌有效期30天
const refreshToken = jwt.sign(refreshPayload, jwtSecret, {
expiresIn: '30d',
issuer: 'whale-town',
audience: 'whale-town-users',
});
// 5. 计算过期时间(秒)