Appearance
故障处理日志
本章节记录系统故障及其修复过程,供后续参考。
2026-01-17:官网 Admin 后台长时间未操作显示空数据
问题描述
用户反馈:官网 Admin 后台长时间不登录后,页面显示空数据,需要刷新或重新登录才能恢复。
表现:
- 统计卡片全部显示 0
- 用户列表为空
- 服务配置列表为空
- 无任何错误提示
根因分析
问题本质:JWT Token 过期后,前端未正确处理 API 返回的 401 错误。
详细链路:
admin_token使用通用的signUserToken(),有效期只有 15 分钟- Cookie 的
maxAge设置的是 24 小时,导致 Cookie 还在但 Token 已过期 - 前端 middleware 只检查 Cookie 是否存在和格式,不验证 Token 是否过期
- API 返回 401 错误后,前端只是
console.error打印日志,没有跳转登录页 - 页面保持空数据状态(
stats和users使用默认空值)
相关文件:
server/utils/jwt.ts:Token 配置accessTokenExpiry: '15m'middleware/admin.ts:只检查 Cookie 存在性pages/admin/index.vue:API 调用的 catch 块未处理 401
解决方案
1. 为 Admin 创建专用长效 Token(7天)
typescript
// server/api/admin/login.post.ts
const ADMIN_TOKEN_EXPIRY = '7d';
const signAdminToken = (user: any): string => {
return jwt.sign({
id: user.id,
user_id: user.id,
username: user.username,
name: user.name,
role: user.role,
type: 'admin'
}, secret, { expiresIn: ADMIN_TOKEN_EXPIRY });
};2. Cookie 有效期与 Token 一致
typescript
setCookie(event, 'admin_token', token, {
httpOnly: false,
maxAge: 60 * 60 * 24 * 7, // 7 天
// ...
});3. 添加 401 错误自动跳转登录
javascript
// pages/admin/index.vue
const handleApiError = (e) => {
if (e?.status === 401 || e?.statusCode === 401) {
console.warn('[Admin] Token 已过期,跳转登录...');
const adminToken = useCookie('admin_token');
adminToken.value = null;
navigateTo('/admin/login');
return true;
}
return false;
};
// 在所有 API 调用的 catch 块中使用
try {
// ...
} catch (e) {
if (!handleApiError(e)) {
console.error('Failed to fetch:', e);
}
}修改文件
| 文件 | 修改内容 |
|---|---|
server/api/admin/login.post.ts | 创建 signAdminToken() 函数,7天有效期 |
pages/admin/index.vue | 添加 handleApiError() 函数处理 401 |
验证方式
- 登录 Admin 后台
- 等待 15 分钟后刷新页面(或手动清除 token)
- 应自动跳转到登录页,而非显示空数据
预防措施
- 所有管理后台 API 调用都应包含 401 错误处理
- Token 有效期 应与业务场景匹配:
- 普通用户:15分钟(安全优先)
- 管理后台:7天(便利优先)
- 考虑添加 Token 过期提示,而非静默失败
2026-01-17:Admin 登录后闪回登录页(附加问题)
问题描述
在修复上述"空数据"问题后,发现新问题:登录成功后页面短暂跳转到 /admin,随即闪回到 /admin/login。
表现:
- 登录请求成功(服务器日志显示 "Login successful")
- Token 正确写入 Cookie
- 页面进入后台后立即闪回登录页
- 控制台显示
[Admin] Token 已过期,跳转登录...
根因分析
问题本质:新签发的 Admin Token 使用了 type: 'admin',但验证函数检查的是 type === 'access'。
详细链路:
signAdminToken()签发的 Token payload 包含type: 'admin'verifyAccessToken()第 65 行检查if (decoded.type !== 'access') return null- Token 验证失败,API 返回 401
- 前端
handleApiError()捕获 401,触发跳转登录
关键代码:
typescript
// server/utils/jwt.ts - 第 62-71 行
export const verifyAccessToken = (token: string) => {
try {
const decoded = jwt.verify(token, getSecret());
if (decoded.type !== 'access') { // ← 这里要求 type 必须是 'access'
return null;
}
return decoded;
} catch (e) {
return null;
}
};解决方案
将 Admin Token 的 type 从 'admin' 改为 'access',保持与验证逻辑兼容:
typescript
// server/api/admin/login.post.ts
const signAdminToken = (user: any): string => {
return jwt.sign({
id: user.id,
user_id: user.id,
username: user.username,
name: user.name,
role: user.role,
type: 'access' // ← 与 verifyAccessToken 验证逻辑保持一致
}, secret, { expiresIn: ADMIN_TOKEN_EXPIRY });
};经验教训
- JWT Token 类型检查:签发新类型 Token 时,必须确认验证函数是否兼容
- 端到端测试:修改认证逻辑后,必须完整测试登录→API调用→登出流程
- 日志辅助定位:控制台输出
Token 已过期,跳转登录虽然误导(实际是 type 不匹配),但帮助定位到 401 触发点
记录人:AI Agent
修复时间:2026-01-17 23:45