Node.js中JWT过期问题:刷新过期令牌返回jwt expired错误
解决JWT身份验证中令牌过期的区分处理问题
你的核心问题在于没有区分JWT的错误类型,不管是令牌过期还是无效签名等其他错误,都统一返回403状态码和相同提示,导致前端无法区分处理逻辑。同时,当前的refresh token机制缺少数据库校验和自动续期的优化,以下是具体解决方案:
1. 区分JWT错误类型,细化返回信息
修改authenticateToken中间件,针对access token的过期和其他验证错误返回不同状态码和提示:
const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (token == null) return res.sendStatus(401); jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => { if (err) { // 单独处理token过期的情况 if (err.name === 'TokenExpiredError') { return res.status(401).json({ message: 'access token已过期,请刷新令牌' }); } // 其他验证错误(如无效签名、篡改)返回403 return res.status(403).json({ message: '无效的access token', error: err.message }); } req.user = user; next(); }); };
2. 优化refresh token的验证逻辑
修改/token接口,不仅区分refresh token的过期错误,还增加数据库校验防止token复用,同时支持自动生成新的refresh token续期:
app.post('/token', (req, res) => { const { token } = req.body; if (!token) return res.sendStatus(401); jwt.verify(token, process.env.REFRESH_TOKEN_SECRET, (err, user) => { if (err) { if (err.name === 'TokenExpiredError') { // refresh token过期,强制用户重新登录 return res.status(401).json({ message: 'refresh token已过期,请重新登录' }); } return res.status(403).json({ message: '无效的refresh token', error: err.message }); } // 校验数据库中存储的refresh token是否与提交的一致,防止被盗用后复用 db.query('SELECT refresh_token FROM tokens WHERE id_user = ?', [user.id], (queryErr, results) => { if (queryErr) throw queryErr; if (results.length === 0 || results[0].refresh_token !== token) { return res.status(403).json({ message: 'refresh token已失效,请重新登录' }); } // 生成新的access token const accessToken = generateAccessToken({ id: user.id, email: user.email }); // 生成新的refresh token并更新数据库,延长会话有效期 const newRefreshToken = generateRefreshToken({ id: user.id, email: user.email }); db.query('UPDATE tokens SET refresh_token = ? WHERE id_user = ?', [newRefreshToken, user.id], (updateErr) => { if (updateErr) throw updateErr; res.json({ accessToken, refreshToken: newRefreshToken }); }); }); }); });
3. 修复登录时的token存储逻辑
原代码中使用UPDATE语句存储refresh token,若用户是首次登录,tokens表中无对应记录会导致存储失败,改为INSERT ... ON DUPLICATE KEY UPDATE:
// 替换login接口中的UPDATE语句 db.query( 'INSERT INTO tokens (id_user, refresh_token) VALUES (?, ?) ON DUPLICATE KEY UPDATE refresh_token = ?', [user.id, refreshToken, refreshToken], (err, result) => { if (err) throw err; res.status(200).json({ result: true, accessToken, refreshToken }); } );
关键优化点说明
- 错误类型区分:通过
err.name判断是TokenExpiredError(令牌过期)还是其他错误,让前端能明确执行对应逻辑(access过期调用刷新接口,refresh过期跳转登录页) - 数据库校验refresh token:确保只有当前有效的refresh token才能刷新access token,避免旧token被盗用后非法获取新令牌
- refresh token自动续期:每次刷新access token时生成新的refresh token,既延长用户会话时间,又降低旧token被盗用的风险
内容的提问来源于stack exchange,提问作者joisberg
相关产品推荐
相关产品推荐

