You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 07:54:05