accessToken过期要求客户端提交refreshToken时应返回什么HTTP状态码
状态码选择建议
首先要明确两个常用认证相关状态码的语义差异,刚好可以解决你担心的混淆问题:
401 Unauthorized:语义为未提供有效身份凭证/身份认证失败,完全匹配token无效、过期的场景,也就是你需要提示客户端刷新token的场景403 Forbidden:语义为身份已认证通过,但当前身份没有访问目标资源的权限,对应你说的用户确实无权限的场景
当前中间件逻辑的优化点
你现有代码的问题是:只要请求携带了无效token就直接返回错误,会拦截所有匿名可访问的路由(比如公开的商品列表、首页接口等),逻辑合理性不足。
推荐采用分层中间件的方案处理,逻辑更清晰也更灵活:
第一步:全局中间件仅做token解码,不做拦截
不管token有效与否,都只负责把解码结果挂载到req对象上,不直接返回响应,所有路由都可以正常走到处理逻辑:
app.use(async (req, res, next)=>{ const token = req.headers.accessToken; req.user = undefined; if(!token) return next(); try{ const user = await jwt.verify(token, SECRET) req.user = user }catch(err){ // 仅记录认证错误,不直接拦截 req.authError = err } next() })
第二步:单独封装认证校验中间件,仅给需要登录的路由使用
需要登录才能访问的路由才引入这个中间件,此时再根据认证结果返回对应响应,同时通过自定义业务码进一步区分错误场景:
// 登录校验中间件 const requireAuth = (req, res, next) => { if(!req.user) { // token过期场景 if(req.authError?.name === 'TokenExpiredError') { return res.status(401).json({ code: 'TOKEN_EXPIRED', message: '访问凭证已过期', needRefresh: true }) } // token无效场景(签名错误、被篡改等) if(req.authError) { return res.status(401).json({ code: 'INVALID_TOKEN', message: '无效的访问凭证', needRefresh: true }) } // 完全未携带token的场景 return res.status(401).json({ code: 'UNAUTHORIZED', message: '需要登录后访问' }) } next() } // 使用示例:需要登录的路由加上该中间件 app.get('/user/info', requireAuth, (req, res) => { res.json(req.user) })
第三步:单独封装权限校验中间件,处理资源权限问题
需要校验用户资源权限的路由额外引入该中间件,此时无权限才返回403,完全和认证错误区分开:
const requirePermission = (permissionKey) => (req, res, next) => { if(!req.user.permissions.includes(permissionKey)) { return res.status(403).json({ code: 'PERMISSION_DENIED', message: '您没有权限访问该资源' }) } next() } // 使用示例:需要删除用户权限的接口 app.delete('/user/:id', requireAuth, requirePermission('delete_user'), (req, res) => { // 处理删除逻辑 })
客户端处理逻辑
客户端可以直接通过状态码做第一层区分:
- 收到
401:判断返回的needRefresh字段,为true就调用refreshToken接口换取新的accessToken,刷新成功后重发原请求;如果refreshToken也失效,直接跳转登录页 - 收到
403:直接给用户展示无权限提示即可,不需要做刷新逻辑
内容的提问来源于stack exchange,提问作者Ganesh Dagadi
相关产品推荐
相关产品推荐

