feat:实现Token过期检查和自动清除机制
主要改动: - 在插件激活时检查Token是否过期,过期则自动清除session - 修复Token检查逻辑,从session.accessToken获取Token而非globalState - 在消息发送前检查Token有效性,过期则提示重新登录 - 优化ICHelperPanel和ICViewProvider的Token过期处理 - 修复退出登录命令名错误(iccoder.logout -> ic-coder.logout) - 添加Token过期检查文档文档
This commit is contained in:
@ -6,11 +6,11 @@
|
||||
* JWT Payload 接口
|
||||
*/
|
||||
export interface JwtPayload {
|
||||
sub?: string; // subject (通常是 userId)
|
||||
userId?: number; // 用户ID (驼峰命名)
|
||||
user_id?: number; // 用户ID (下划线命名)
|
||||
exp?: number; // 过期时间
|
||||
iat?: number; // 签发时间
|
||||
sub?: string; // subject (通常是 userId)
|
||||
userId?: number; // 用户ID (驼峰命名)
|
||||
user_id?: number; // 用户ID (下划线命名)
|
||||
exp?: number; // 过期时间
|
||||
iat?: number; // 签发时间
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@ -21,9 +21,9 @@ export interface JwtPayload {
|
||||
*/
|
||||
export function parseJwtPayload(token: string): JwtPayload | null {
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) {
|
||||
console.warn('[JWT] token 格式不正确,期望3部分,实际:', parts.length);
|
||||
console.warn("[JWT] token 格式不正确,期望3部分,实际:", parts.length);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -31,17 +31,17 @@ export function parseJwtPayload(token: string): JwtPayload | null {
|
||||
const payload = parts[1];
|
||||
|
||||
// base64url 转 base64
|
||||
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
|
||||
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
||||
|
||||
// 解码
|
||||
const jsonStr = Buffer.from(base64, 'base64').toString('utf-8');
|
||||
const jsonStr = Buffer.from(base64, "base64").toString("utf-8");
|
||||
const parsed = JSON.parse(jsonStr);
|
||||
|
||||
console.log('[JWT] 解析成功, payload 字段:', Object.keys(parsed));
|
||||
console.log('[JWT] payload 内容:', JSON.stringify(parsed));
|
||||
console.log("[JWT] 解析成功, payload 字段:", Object.keys(parsed));
|
||||
console.log("[JWT] payload 内容:", JSON.stringify(parsed));
|
||||
return parsed;
|
||||
} catch (error) {
|
||||
console.error('[JWT] 解析失败:', error);
|
||||
console.error("[JWT] 解析失败:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ export function getUserIdFromToken(token: string): string | null {
|
||||
return String(payload.sub);
|
||||
}
|
||||
|
||||
console.warn('[JWT] payload 中没有 user_id, userId 或 sub 字段');
|
||||
console.warn("[JWT] payload 中没有 user_id, userId 或 sub 字段");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -78,14 +78,17 @@ export function getUserIdFromToken(token: string): string | null {
|
||||
* @param bufferSeconds 提前多少秒判定为过期(默认60秒)
|
||||
* @returns true 表示已过期,false 表示未过期,null 表示无法判断
|
||||
*/
|
||||
export function isTokenExpired(token: string, bufferSeconds: number = 60): boolean | null {
|
||||
export function isTokenExpired(
|
||||
token: string,
|
||||
bufferSeconds: number = 60,
|
||||
): boolean | null {
|
||||
const payload = parseJwtPayload(token);
|
||||
if (!payload) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (payload.exp === undefined) {
|
||||
console.warn('[JWT] payload 中没有 exp 字段,无法判断过期');
|
||||
console.warn("[JWT] payload 中没有 exp 字段,无法判断过期");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -94,7 +97,7 @@ export function isTokenExpired(token: string, bufferSeconds: number = 60): boole
|
||||
const isExpired = now >= expTime;
|
||||
|
||||
if (isExpired) {
|
||||
console.warn('[JWT] token 已过期,exp:', payload.exp, '当前:', now);
|
||||
console.warn("[JWT] token 已过期,exp:", payload.exp, "当前:", now);
|
||||
}
|
||||
|
||||
return isExpired;
|
||||
|
||||
@ -18,6 +18,7 @@ import { ChatHistoryManager } from "./chatHistoryManager";
|
||||
import { dialogManager, DialogSession } from "../services/dialogService";
|
||||
import { userInteractionManager } from "../services/userInteraction";
|
||||
import { healthCheck } from "../services/apiClient";
|
||||
import { isTokenExpired } from "./jwtUtils";
|
||||
import {
|
||||
checkBalanceBeforeSend,
|
||||
fetchBalance,
|
||||
@ -47,6 +48,83 @@ export async function handleUserMessage(
|
||||
) {
|
||||
console.log("收到用户消息:", text);
|
||||
|
||||
// 检查 token 是否过期
|
||||
const context = (panel as any).__context;
|
||||
if (context) {
|
||||
// 从 session 中获取 token
|
||||
let token: string | undefined;
|
||||
try {
|
||||
const session = await vscode.authentication.getSession("iccoder", [], { createIfNone: false });
|
||||
token = session?.accessToken;
|
||||
} catch (error) {
|
||||
console.warn("[MessageHandler] 获取 session 失败:", error);
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
console.warn("[MessageHandler] 未登录,阻止发送");
|
||||
|
||||
// 保存待发送的消息
|
||||
await context.globalState.update('pendingMessage', {
|
||||
text,
|
||||
mode,
|
||||
serviceTier,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// 显示弹窗提示
|
||||
const action = await vscode.window.showWarningMessage(
|
||||
'请先登录后再发送消息',
|
||||
'立即登录'
|
||||
);
|
||||
|
||||
if (action === '立即登录') {
|
||||
vscode.commands.executeCommand("ic-coder.login");
|
||||
}
|
||||
|
||||
// 恢复输入状态
|
||||
panel.webview.postMessage({
|
||||
command: "updateSegments",
|
||||
segments: [],
|
||||
isComplete: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTokenExpired(token)) {
|
||||
console.warn("[MessageHandler] Token 已过期,阻止发送");
|
||||
|
||||
// 保存待发送的消息
|
||||
await context.globalState.update('pendingMessage', {
|
||||
text,
|
||||
mode,
|
||||
serviceTier,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// 清除过期的 session
|
||||
await context.globalState.update('icCoderSessions', []);
|
||||
await context.globalState.update('icCoderUserInfo', undefined);
|
||||
|
||||
// 显示弹窗提示
|
||||
const action = await vscode.window.showWarningMessage(
|
||||
'登录已过期,请重新登录',
|
||||
'立即登录'
|
||||
);
|
||||
|
||||
if (action === '立即登录') {
|
||||
vscode.commands.executeCommand("ic-coder.login");
|
||||
}
|
||||
|
||||
// 恢复输入状态
|
||||
panel.webview.postMessage({
|
||||
command: "updateSegments",
|
||||
segments: [],
|
||||
isComplete: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 记录用户消息到历史(允许失败,不阻塞主流程)
|
||||
try {
|
||||
const historyManager = ChatHistoryManager.getInstance();
|
||||
|
||||
Reference in New Issue
Block a user