# Token 过期检查实现方案 ## 1. 概述 实现三个关键时机的 Token 过期检查: - 插件激活时 - 发起 API 请求前 - 用户交互时(打开面板/侧边栏) ## 2. 数据存储 ### 2.1 存储位置 使用 VS Code 的 `globalState` 存储: ```typescript context.globalState.update('tokenExp', exp); ``` ### 2.2 存储内容 - `token`: 用户 token - `tokenExp`: 过期时间戳(秒) - `userInfo`: 用户信息 ## 3. 核心函数设计 ### 3.1 过期检查函数 ```typescript /** * 检查 token 是否过期 * @param exp - 过期时间戳(秒) * @param bufferSeconds - 提前判断过期的缓冲时间(默认 60 秒) * @returns true 表示已过期或即将过期 */ function isTokenExpired(exp: number | undefined, bufferSeconds: number = 60): boolean { if (!exp) { return true; // 没有过期时间,视为已过期 } const now = Math.floor(Date.now() / 1000); // 当前时间戳(秒) return now >= (exp - bufferSeconds); // 提前 60 秒判断过期 } ``` ### 3.2 清除登录状态函数 ```typescript /** * 清除所有登录相关状态 */ async function clearAuthState(context: vscode.ExtensionContext): Promise { await context.globalState.update('token', undefined); await context.globalState.update('tokenExp', undefined); await context.globalState.update('userInfo', undefined); } ``` ### 3.3 统一过期处理函数 ```typescript /** * 处理 token 过期情况 * @param context - 扩展上下文 * @param showMessage - 是否显示提示消息 */ async function handleTokenExpired( context: vscode.ExtensionContext, showMessage: boolean = true ): Promise { await clearAuthState(context); if (showMessage) { const action = await vscode.window.showWarningMessage( '登录已过期,请重新登录', '立即登录' ); if (action === '立即登录') { // 触发登录流程(打开登录面板) vscode.commands.executeCommand('ic-coder.openPanel'); } } } ``` ## 4. 三个检查时机实现 ### 4.1 插件激活时检查 **位置**: `src/extension.ts` 的 `activate` 函数 **实现**: ```typescript export async function activate(context: vscode.ExtensionContext) { console.log('IC Coder 插件正在激活...'); // 1. 检查 token 是否过期 const tokenExp = context.globalState.get('tokenExp'); if (isTokenExpired(tokenExp)) { // 静默清除,不显示提示(避免启动时打扰用户) await handleTokenExpired(context, false); } // ... 其他激活逻辑 } ``` **说明**: 启动时静默检查,如果过期则清除状态,但不弹窗提示 --- ### 4.2 发起 API 请求前检查 **位置**: `src/utils/messageHandler.ts` 的 API 请求函数 **实现**: ```typescript // 在发送消息到后端前检查 async function sendMessageToBackend(message: string, context: vscode.ExtensionContext) { // 1. 检查 token 是否过期 const tokenExp = context.globalState.get('tokenExp'); if (isTokenExpired(tokenExp)) { await handleTokenExpired(context, true); // 显示提示 return; // 中断请求 } const token = context.globalState.get('token'); if (!token) { vscode.window.showWarningMessage('请先登录'); return; } // 2. 继续发送请求 // ... 原有请求逻辑 } ``` **说明**: 每次 API 请求前检查,如果过期则提示用户并中断请求 --- ### 4.3 用户交互时检查 **位置**: - `src/panels/ICHelperPanel.ts` - 打开聊天面板时 - `src/views/ICViewProvider.ts` - 侧边栏视图加载时 **实现 - 聊天面板**: ```typescript // ICHelperPanel.ts public static render(extensionUri: vscode.Uri, context: vscode.ExtensionContext) { // 1. 检查 token 是否过期 const tokenExp = context.globalState.get('tokenExp'); if (isTokenExpired(tokenExp)) { handleTokenExpired(context, true); // 显示提示 // 继续渲染面板,但会显示未登录状态 } // 2. 创建或显示面板 // ... 原有逻辑 } ``` **实现 - 侧边栏视图**: ```typescript // ICViewProvider.ts public resolveWebviewView(webviewView: vscode.WebviewView) { // 1. 检查 token 是否过期 const tokenExp = this._context.globalState.get('tokenExp'); if (isTokenExpired(tokenExp)) { handleTokenExpired(this._context, false); // 静默清除 // 继续渲染,显示未登录状态 } // 2. 渲染视图 // ... 原有逻辑 } ``` **说明**: 打开面板时检查,聊天面板显示提示,侧边栏静默处理 ## 5. 后端响应处理 ### 5.1 保存 exp 字段 **位置**: `src/utils/messageHandler.ts` 处理登录响应的地方 **实现**: ```typescript // 处理登录成功响应 if (response.data.token) { await context.globalState.update('token', response.data.token); // 保存过期时间 if (response.data.exp) { await context.globalState.update('tokenExp', response.data.exp); } // 保存用户信息 if (response.data.userInfo) { await context.globalState.update('userInfo', response.data.userInfo); } } ``` ### 5.2 处理 401 响应 **实现**: ```typescript // API 请求错误处理 if (error.response?.status === 401) { // 后端返回 401,说明 token 无效或过期 await handleTokenExpired(context, true); return; } ``` ## 6. 工具函数位置 建议创建新文件 `src/utils/authHelper.ts`: ```typescript import * as vscode from 'vscode'; export function isTokenExpired(exp: number | undefined, bufferSeconds: number = 60): boolean { if (!exp) { return true; } const now = Math.floor(Date.now() / 1000); return now >= (exp - bufferSeconds); } export async function clearAuthState(context: vscode.ExtensionContext): Promise { await context.globalState.update('token', undefined); await context.globalState.update('tokenExp', undefined); await context.globalState.update('userInfo', undefined); } export async function handleTokenExpired( context: vscode.ExtensionContext, showMessage: boolean = true ): Promise { await clearAuthState(context); if (showMessage) { const action = await vscode.window.showWarningMessage( '登录已过期,请重新登录', '立即登录' ); if (action === '立即登录') { vscode.commands.executeCommand('ic-coder.openPanel'); } } } ``` ## 7. 测试场景 1. **启动测试**: 设置过期的 exp,重启插件,验证状态被清除 2. **请求测试**: 设置即将过期的 exp,发送消息,验证被拦截 3. **交互测试**: 设置过期的 exp,打开面板,验证提示显示 4. **401 测试**: 模拟后端返回 401,验证状态清除 ## 8. 注意事项 - 使用 60 秒缓冲时间,避免请求中途过期 - 启动和侧边栏加载时静默处理,避免打扰用户 - 主动操作(发消息、打开聊天面板)时显示提示 - 所有时间戳使用秒为单位(与后端保持一致) - 过期检查应该在所有需要 token 的操作前执行 ## 9. 修改文件清单 需要修改的文件: 1. **新建**: `src/utils/authHelper.ts` - 认证辅助工具函数 2. **修改**: `src/extension.ts` - 插件激活时检查 3. **修改**: `src/utils/messageHandler.ts` - API 请求前检查 + 保存 exp + 处理 401 4. **修改**: `src/panels/ICHelperPanel.ts` - 打开聊天面板时检查 5. **修改**: `src/views/ICViewProvider.ts` - 侧边栏加载时检查