添加jwt验证,添加交叉评查首页加载对接接口,评查任务文档列表对接接口,意见列表对接接口

This commit is contained in:
2025-07-22 14:37:37 +08:00
parent de953283e3
commit 47664fc0e8
19 changed files with 1988 additions and 557 deletions
+191
View File
@@ -0,0 +1,191 @@
/**
* JWT工具类
* 用于生成和验证前端专用的JWT Token
*
* 主要功能:
* - 生成包含用户信息的JWT
* - 验证JWT的有效性
* - 解析JWT获取用户信息
*/
import jwt from 'jsonwebtoken';
const { sign, verify, decode } = jwt;
// JWT密钥 - 在生产环境中应该从环境变量读取
const JWT_SECRET = 'gdyc-super-secrets-jjwtt-key-change-this-in-production-20250721-from-login-callback';
// JWT配置
const JWT_CONFIG = {
algorithm: 'HS256' as const,
issuer: 'docreview-system',
audience: 'docreview-frontend'
};
// JWT载荷接口
export interface JWTPayload {
// 标准字段
sub: string; // 用户唯一标识(来自IDaaS)
iss: string; // 发行者
aud: string; // 受众
iat: number; // 签发时间
exp: number; // 过期时间
// 自定义用户信息字段
user_id: string; // 数据库中的用户ID
username: string; // 用户名
nick_name: string; // 用户昵称
email?: string; // 邮箱
phone_number?: string; // 手机号
ou_id: string; // 组织单位ID
ou_name: string; // 组织单位名称
is_leader: boolean; // 是否为负责人
user_role: string; // 用户角色
}
// 用户信息接口(用于生成JWT
export interface UserInfoForJWT {
sub: string;
user_id: string;
username: string;
nick_name: string;
email?: string;
phone_number?: string;
ou_id: string;
ou_name: string;
is_leader: boolean;
user_role: string;
}
/**
* JWT工具类
*/
export class JWTUtils {
/**
* 生成JWT
* @param userInfo 用户信息
* @param expiresIn 过期时间(秒),默认为OAuth token过期时间的90%
* @returns JWT字符串
*/
static generateJWT(userInfo: UserInfoForJWT, expiresIn: number): string {
const now = Math.floor(Date.now() / 1000);
// 将过期时间设置为OAuth token过期时间的90%,确保JWT在OAuth token之前过期
const jwtExpiresIn = Math.floor(expiresIn);
const payload: JWTPayload = {
// 标准字段
sub: userInfo.sub,
iss: JWT_CONFIG.issuer,
aud: JWT_CONFIG.audience,
iat: now,
exp: now + jwtExpiresIn,
// 用户信息字段
user_id: userInfo.user_id,
username: userInfo.username,
nick_name: userInfo.nick_name,
email: userInfo.email,
phone_number: userInfo.phone_number,
ou_id: userInfo.ou_id,
ou_name: userInfo.ou_name,
is_leader: userInfo.is_leader,
user_role: userInfo.user_role
};
return sign(payload, JWT_SECRET, {
algorithm: JWT_CONFIG.algorithm
});
}
/**
* 验证JWT
* @param token JWT字符串
* @returns 验证结果和载荷
*/
static verifyJWT(token: string): { valid: boolean; payload?: JWTPayload; error?: string } {
try {
const payload = verify(token, JWT_SECRET, {
algorithms: [JWT_CONFIG.algorithm],
issuer: JWT_CONFIG.issuer,
audience: JWT_CONFIG.audience
}) as JWTPayload;
return { valid: true, payload };
} catch (error) {
if (error instanceof Error) {
return { valid: false, error: error.message };
}
return { valid: false, error: 'JWT验证失败' };
}
}
/**
* 解析JWT(不验证签名)
* @param token JWT字符串
* @returns 解析结果
*/
static decodeJWT(token: string): { payload?: JWTPayload; error?: string } {
try {
const payload = decode(token) as JWTPayload;
return { payload };
} catch (error) {
return { error: 'JWT解析失败' };
}
}
/**
* 检查JWT是否即将过期
* @param token JWT字符串
* @param bufferMinutes 缓冲时间(分钟),默认5分钟
* @returns 是否即将过期
*/
static isJWTExpiringSoon(token: string, bufferMinutes: number = 5): boolean {
const decoded = this.decodeJWT(token);
if (!decoded.payload) {
return true; // 解析失败视为过期
}
const now = Math.floor(Date.now() / 1000);
const bufferSeconds = bufferMinutes * 60;
return decoded.payload.exp <= (now + bufferSeconds);
}
/**
* 获取JWT过期时间
* @param token JWT字符串
* @returns 过期时间戳
*/
static getJWTExpiration(token: string): number | null {
const decoded = this.decodeJWT(token);
return decoded.payload?.exp || null;
}
/**
* 从JWT中提取用户信息
* @param token JWT字符串
* @returns 用户信息
*/
static extractUserInfo(token: string): UserInfoForJWT | null {
const verification = this.verifyJWT(token);
if (!verification.valid || !verification.payload) {
return null;
}
const payload = verification.payload;
return {
sub: payload.sub,
user_id: payload.user_id,
username: payload.username,
nick_name: payload.nick_name,
email: payload.email,
phone_number: payload.phone_number,
ou_id: payload.ou_id,
ou_name: payload.ou_name,
is_leader: payload.is_leader,
user_role: payload.user_role
};
}
}
export default JWTUtils;