5月27日 23:08

JWT 错误处理应该怎么做?

JWT 错误处理的核心思路是:按错误类型分类响应,统一错误格式,前端拦截自动续签

jsonwebtokenjwt.verify() 抛出的错误主要有三种:TokenExpiredError(过期)、JsonWebTokenError(格式/签名无效)、NotBeforeError(nbf 未生效)。处理方式不是逐个 try-catch,而是在一个 catch 中按 error.name 分发。

后端:统一错误分发

javascript
function handleJwtError(error) { const map = { TokenExpiredError: { status: 401, code: 'TOKEN_EXPIRED', msg: '令牌已过期' }, JsonWebTokenError: { status: 401, code: 'INVALID_TOKEN', msg: '令牌无效' }, NotBeforeError: { status: 401, code: 'TOKEN_NOT_ACTIVE', msg: '令牌尚未生效' }, }; return map[error.name] || { status: 500, code: 'AUTH_ERROR', msg: '认证失败' }; } function authMiddleware(req, res, next) { const token = (req.headers.authorization || '').replace('Bearer ', ''); if (!token) return res.status(401).json({ code: 'MISSING_TOKEN', msg: '未提供令牌' }); try { req.user = jwt.verify(token, SECRET, { algorithms: ['HS256'] }); next(); } catch (e) { const err = handleJwtError(e); res.status(err.status).json({ code: err.code, msg: err.msg }); } }

关键点:jwt.verifyalgorithms 参数必须显式指定,防止算法混淆攻击(none 算法绕过)。

前端:拦截 401 并自动续签

javascript
api.interceptors.response.use( res => res, async error => { const { config, response } = error; if (response?.status === 401 && !config._retry) { if (response.data.code === 'TOKEN_EXPIRED') { config._retry = true; try { const { data } = await axios.post('/auth/refresh', { refreshToken: localStorage.getItem('refreshToken') }); localStorage.setItem('accessToken', data.accessToken); config.headers.Authorization = `Bearer ${data.accessToken}`; return api(config); } catch { // refresh 也失败,必须重新登录 } } localStorage.clear(); window.location.href = '/login'; } return Promise.reject(error); } );

并发请求时多个 401 会同时触发 refresh,需要用 Promise 队列去重,只发一次 refresh 请求,其余请求等结果复用新 token。

刷新 Token 的错误边界

Refresh token 过期或被撤销(黑名单)时,服务端直接返回 LOGIN_REQUIRED,前端跳登录页,不再重试。不要对 JWT 验证错误做自动重试——验证是幂等的,失败就是失败,重试没有意义。

安全注意事项

  • 错误消息不要暴露内部实现(如密钥名、算法细节),对外只返回业务错误码
  • 验证失败要记日志,包含 IP、路径、UA,用于检测暴力破解和 token 嗅探
  • 生产环境用 HttpOnly Cookie 存 token,避免 XSS 读取 localStorage

追问:Token 过期但用户正在操作怎么办?

两种方案:一是前端在 token 快过期时静默 refresh(如距过期 5 分钟主动续签);二是后端在响应头加 X-Token-Will-Expire,前端收到后触发刷新。两者都依赖 refresh token 仍有效的前提。

标签:JWT