116 lines
3.4 KiB
TypeScript
Executable File
116 lines
3.4 KiB
TypeScript
Executable File
// apps/web/lib/token-manager.ts
|
|
interface TokenData {
|
|
access_token: string;
|
|
refresh_token?: string;
|
|
expires_at: number;
|
|
token_type: string;
|
|
}
|
|
|
|
export class TokenManager {
|
|
private static readonly ACCESS_TOKEN_KEY = 'oidc_access_token';
|
|
private static readonly REFRESH_TOKEN_KEY = 'oidc_refresh_token';
|
|
private static readonly EXPIRES_AT_KEY = 'oidc_expires_at';
|
|
private static readonly TOKEN_TYPE_KEY = 'oidc_token_type';
|
|
|
|
/**
|
|
* 保存令牌到本地存储
|
|
*/
|
|
static saveTokens(tokenResponse: {
|
|
access_token: string;
|
|
refresh_token?: string;
|
|
expires_in: number;
|
|
token_type: string;
|
|
}): void {
|
|
const expiresAt = Date.now() + (tokenResponse.expires_in * 1000);
|
|
|
|
localStorage.setItem(this.ACCESS_TOKEN_KEY, tokenResponse.access_token);
|
|
localStorage.setItem(this.EXPIRES_AT_KEY, expiresAt.toString());
|
|
localStorage.setItem(this.TOKEN_TYPE_KEY, tokenResponse.token_type);
|
|
|
|
if (tokenResponse.refresh_token) {
|
|
localStorage.setItem(this.REFRESH_TOKEN_KEY, tokenResponse.refresh_token);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取访问令牌
|
|
*/
|
|
static getAccessToken(): string | null {
|
|
return localStorage.getItem(this.ACCESS_TOKEN_KEY);
|
|
}
|
|
|
|
/**
|
|
* 获取刷新令牌
|
|
*/
|
|
static getRefreshToken(): string | null {
|
|
return localStorage.getItem(this.REFRESH_TOKEN_KEY);
|
|
}
|
|
|
|
/**
|
|
* 检查令牌是否过期
|
|
*/
|
|
static isTokenExpired(): boolean {
|
|
const expiresAt = localStorage.getItem(this.EXPIRES_AT_KEY);
|
|
if (!expiresAt) return true;
|
|
|
|
return Date.now() >= parseInt(expiresAt);
|
|
}
|
|
|
|
/**
|
|
* 获取有效的访问令牌(自动刷新)
|
|
*/
|
|
static async getValidAccessToken(): Promise<string | null> {
|
|
const accessToken = this.getAccessToken();
|
|
if (!accessToken) return null;
|
|
|
|
if (!this.isTokenExpired()) {
|
|
return accessToken;
|
|
}
|
|
|
|
// 尝试刷新令牌
|
|
const refreshToken = this.getRefreshToken();
|
|
if (refreshToken) {
|
|
try {
|
|
const { oidcClient } = await import('./oidc-client');
|
|
const newTokens = await oidcClient.refreshToken(refreshToken);
|
|
this.saveTokens(newTokens);
|
|
return newTokens.access_token;
|
|
} catch (error) {
|
|
console.error('刷新令牌失败:', error);
|
|
this.clearTokens();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 清除所有令牌
|
|
*/
|
|
static clearTokens(): void {
|
|
localStorage.removeItem(this.ACCESS_TOKEN_KEY);
|
|
localStorage.removeItem(this.REFRESH_TOKEN_KEY);
|
|
localStorage.removeItem(this.EXPIRES_AT_KEY);
|
|
localStorage.removeItem(this.TOKEN_TYPE_KEY);
|
|
}
|
|
|
|
/**
|
|
* 获取完整的令牌数据
|
|
*/
|
|
static getTokenData(): TokenData | null {
|
|
const accessToken = this.getAccessToken();
|
|
if (!accessToken) return null;
|
|
|
|
const refreshToken = this.getRefreshToken();
|
|
const expiresAt = localStorage.getItem(this.EXPIRES_AT_KEY);
|
|
const tokenType = localStorage.getItem(this.TOKEN_TYPE_KEY) || 'Bearer';
|
|
|
|
return {
|
|
access_token: accessToken,
|
|
refresh_token: refreshToken || undefined,
|
|
expires_at: expiresAt ? parseInt(expiresAt) : 0,
|
|
token_type: tokenType,
|
|
};
|
|
}
|
|
} |