Files
leaudit-platform-frontend/app/api/login/token-manager.server.ts
T

154 lines
4.2 KiB
TypeScript

/**
* Token管理服务
* 负责处理OAuth访问令牌的刷新和管理
* 如果需要添加新的Token相关功能:
* 1. 优先考虑在 `TokenManager` 中添加
* 2. 如果需要新的网络请求,在 `OAuthClient` 中添加
*/
import { OAuthClient } from "./oauth-client";
import { OAUTH_CONFIG } from "~/config/api-config";
interface TokenInfo {
accessToken: string;
refreshToken: string;
tokenIssuedAt: number;
tokenExpiresIn: number;
}
interface RefreshResult {
success: boolean;
newTokenInfo?: TokenInfo;
error?: string;
}
/**
* Token管理服务
* 负责处理OAuth访问令牌的刷新和管理
*/
export class TokenManager {
private oauthClient: OAuthClient;
constructor() {
this.oauthClient = new OAuthClient(OAUTH_CONFIG.value);
}
/**
* 检查token是否过期
*/
isTokenExpired(tokenInfo: TokenInfo): boolean {
const now = Date.now();
const expiresAt = tokenInfo.tokenIssuedAt + (tokenInfo.tokenExpiresIn * 1000);
return now >= expiresAt;
}
/**
* 检查token是否需要刷新(提前5分钟)
*/
shouldRefreshToken(tokenInfo: TokenInfo, refreshThresholdMinutes: number = 5): boolean {
const now = Date.now();
const expiresAt = tokenInfo.tokenIssuedAt + (tokenInfo.tokenExpiresIn * 1000);
const refreshThreshold = refreshThresholdMinutes * 60 * 1000;
return now >= (expiresAt - refreshThreshold);
}
/**
* 获取token剩余有效时间(秒)
*/
getTokenRemainingTime(tokenInfo: TokenInfo): number {
const now = Date.now();
const expiresAt = tokenInfo.tokenIssuedAt + (tokenInfo.tokenExpiresIn * 1000);
return Math.floor((expiresAt - now) / 1000);
}
/**
* 刷新访问令牌
*/
async refreshToken(refreshToken: string): Promise<RefreshResult> {
try {
console.log("开始刷新访问令牌...");
const newTokenResponse = await this.oauthClient.refreshAccessToken(refreshToken);
if (!newTokenResponse) {
console.error("刷新令牌失败:服务器返回空响应");
return {
success: false,
error: "服务器返回空响应"
};
}
const newTokenInfo: TokenInfo = {
accessToken: newTokenResponse.access_token,
refreshToken: newTokenResponse.refresh_token,
tokenIssuedAt: Date.now(),
tokenExpiresIn: newTokenResponse.expires_in
};
console.log(`令牌刷新成功,新令牌有效期: ${newTokenResponse.expires_in}`);
return {
success: true,
newTokenInfo
};
} catch (error) {
console.error("刷新令牌时发生错误:", error);
return {
success: false,
error: error instanceof Error ? error.message : "未知错误"
};
}
}
/**
* 检查并自动刷新token(如果需要)
*/
async checkAndRefreshToken(tokenInfo: TokenInfo): Promise<RefreshResult> {
// 如果token已过期,必须刷新
if (this.isTokenExpired(tokenInfo)) {
console.log("Token已过期,尝试刷新...");
return this.refreshToken(tokenInfo.refreshToken);
}
// 如果token即将过期,主动刷新
if (this.shouldRefreshToken(tokenInfo)) {
const remainingTime = this.getTokenRemainingTime(tokenInfo);
console.log(`Token将在${remainingTime}秒后过期,主动刷新...`);
return this.refreshToken(tokenInfo.refreshToken);
}
// Token仍然有效,无需刷新
return {
success: true,
newTokenInfo: tokenInfo
};
}
/**
* 格式化token到期时间
*/
formatTokenExpiry(tokenInfo: TokenInfo): string {
const expiresAt = new Date(tokenInfo.tokenIssuedAt + (tokenInfo.tokenExpiresIn * 1000));
return expiresAt.toLocaleString('zh-CN');
}
/**
* 获取token状态信息(用于调试)
*/
getTokenStatus(tokenInfo: TokenInfo): {
isExpired: boolean;
shouldRefresh: boolean;
remainingTime: number;
expiryTime: string;
} {
return {
isExpired: this.isTokenExpired(tokenInfo),
shouldRefresh: this.shouldRefreshToken(tokenInfo),
remainingTime: this.getTokenRemainingTime(tokenInfo),
expiryTime: this.formatTokenExpiry(tokenInfo)
};
}
}
// 导出单例实例
export const tokenManager = new TokenManager();