Skip to content

控制台在高负载的情况下登陆成功后自动退出登陆,无限循环 #2912

@fmnx

Description

@fmnx

前端把某些高负载下的鉴权失败当成了“登录失效”,然后清空 token 并跳转登录页

核心链路是:

  1. 控制台登录/进入页面后会立刻请求当前用户信息
    frontend/src/stores/auth.ts:117 的 checkAuth() 会加载本地 token 后立即 refreshUser(),也就是打 /auth/me。
    另外 frontend/src/stores/auth.ts:362 的 setToken() 也会立刻拉一次用户信息。

  2. 任何接口返回 401,前端都会尝试刷新 token
    逻辑在 frontend/src/api/client.ts:151。
    如果刷新失败,前端会清空 auth_token、refresh_token、auth_user,然后跳转 /login:
    frontend/src/api/client.ts:222

  3. 高负载时,后端鉴权中间件可能把数据库/服务异常误判成 401 USER_NOT_FOUND
    这里是最可疑点。
    backend/internal/server/middleware/jwt_auth.go:60 里 userService.GetByID() 只要报错,就直接返回 401 USER_NOT_FOUND。
    管理端鉴权也一样:backend/internal/server/middleware/admin_auth.go:171

也就是说,高负载时如果查用户超时、数据库连接池打满、查询失败,这里没有区分“用户真的不存在”和“数据库临时不可用”,而是统一返回 401。前端收到 401 后会走刷新 token;如果刷新也失败,就直接
清 token 自动登出。

还有一个放大因素:/auth/refresh 本身加了 Redis 限流,而且是 fail-close:

  • 路由限流在 backend/internal/server/routes/auth.go:42
  • Redis/限流异常时会返回 429,见 backend/internal/middleware/rate_limiter.go:99
  • 刷新 token 还会访问 Redis 和数据库,见 backend/internal/service/auth_service.go:1500

所以高负载下可能是:

/auth/me 查用户失败 -> 返回 401 -> 前端刷新 token -> /auth/refresh 因 Redis/DB/限流失败 -> 前端清空登录态 -> 自动回登录页。

建议:

  1. 后端鉴权中间件不要把 GetByID 的所有错误都返回 401。只有用户不存在、禁用、token version 不匹配才返回 401;数据库超时/连接失败应该返回 503 或 500。
  2. 前端刷新 token 失败时,不要对 429/500/503/network error 直接清空登录态。这些更像临时服务异常,应该保留 token,提示稍后重试。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions