在Next.js(自定义Node.js+Express服务器)服务端实现刷新令牌(Refresh Token)功能的方法咨询
在Next.js(自定义Node.js+Express服务器)服务端实现刷新令牌(Refresh Token)功能的方法咨询
我完全懂你的需求啦——你想在Next.js的服务端(也就是page.js里调用API的那部分)实现刷新令牌(Refresh Token)的功能,而且用的是自定义的Node.js+Express服务器,不是Next.js自带的后端。结合你的场景,我给你梳理下具体的实现思路和代码示例:
一、封装服务端专用的Axios实例(带刷新拦截器)
首先我们要在服务端单独封装一个Axios实例,配上响应拦截器,用来处理令牌过期后的自动刷新逻辑:
// 新建服务端工具文件,比如utils/serverAxios.js import axios from 'axios'; // 创建服务端专属的Axios实例 const serverAxios = axios.create({ baseURL: '你的业务API基础地址', }); // 定义刷新AccessToken的函数 const refreshAccessToken = async () => { // 这里要从安全的服务端存储获取Refresh Token,比如用户会话、Redis或者加密的数据库字段 // 注意:绝对不能在前端暴露Refresh Token,全程在服务端处理 const refreshToken = '从服务端安全存储中获取的Refresh Token'; try { // 调用你的Refresh Token接口 const response = await axios.post('/api/refresh-token', { refreshToken }); const newAccessToken = response.data.accessToken; // 把新的AccessToken存回服务端的安全存储(比如更新用户会话) return newAccessToken; } catch (error) { // 刷新失败,说明Refresh Token也失效了,需要引导用户重新登录 throw new Error('Refresh Token已失效,请重新登录'); } }; // 添加响应拦截器,处理令牌过期的情况 serverAxios.interceptors.response.use( (response) => response, // 请求成功直接返回 async (error) => { const originalRequest = error.config; // 判断是否是AccessToken过期导致的401,并且还没重试过(防止无限循环) if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; // 标记已重试,避免循环 try { // 获取新的AccessToken const newAccessToken = await refreshAccessToken(); // 更新原请求的Authorization头 originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`; // 重试原请求 return serverAxios(originalRequest); } catch (refreshError) { // 刷新失败,这里可以清理用户会话,然后重定向到登录页 return Promise.reject(refreshError); } } // 其他错误直接抛出 return Promise.reject(error); } ); export default serverAxios;
二、在page.js的服务端逻辑中使用封装好的Axios
接下来在你的page.js里,用这个封装好的Axios实例来调用API,就能自动处理令牌刷新了:
// 比如app/page.js 或者 pages目录下的page.js(根据你的Next.js版本) import serverAxios from '../utils/serverAxios'; export async function getServerSideProps(context) { try { // 从服务端会话中获取初始的AccessToken const initialAccessToken = context.req.session.accessToken; // 用封装好的服务端Axios调用受保护的API const response = await serverAxios.get('/api/protected-data', { headers: { Authorization: `Bearer ${initialAccessToken}` } }); return { props: { data: response.data } }; } catch (error) { // 处理错误:比如刷新失败,直接重定向到登录页 return { redirect: { destination: '/login', permanent: false } }; } } export default function Home({ data }) { return <div>获取到的服务端数据:{JSON.stringify(data)}</div>; }
三、Express服务器端实现Refresh Token接口
最后要在你的Express服务器上写一个Refresh Token的接口,用来生成新的AccessToken:
// Express路由文件,比如routes/auth.js const express = require('express'); const router = express.Router(); const jwt = require('jsonwebtoken'); // 实际项目建议用Redis或者数据库存储Refresh Token,这里用Map做示例 const refreshTokenStore = new Map(); router.post('/api/refresh-token', async (req, res) => { const { refreshToken } = req.body; if (!refreshToken) { return res.status(400).json({ message: 'Refresh Token缺失' }); } try { // 验证Refresh Token的有效性 const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET); // 检查Refresh Token是否在存储中(防止被提前撤销) if (refreshTokenStore.get(decoded.userId) !== refreshToken) { return res.status(401).json({ message: 'Refresh Token无效' }); } // 生成新的AccessToken(有效期短一点,比如15分钟) const newAccessToken = jwt.sign( { userId: decoded.userId }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' } ); // 可选:令牌旋转机制——生成新的Refresh Token,旧的失效(提升安全性) const newRefreshToken = jwt.sign( { userId: decoded.userId }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' } ); // 更新存储中的Refresh Token refreshTokenStore.set(decoded.userId, newRefreshToken); // 用HttpOnly Cookie返回新的Refresh Token(比返回在响应体里更安全,防止XSS攻击) res.cookie('refreshToken', newRefreshToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', // 生产环境开启HTTPS sameSite: 'strict', maxAge: 7 * 24 * 60 * 60 * 1000 // 有效期7天 }); // 返回新的AccessToken res.json({ accessToken: newAccessToken }); } catch (error) { res.status(401).json({ message: 'Refresh Token已失效,请重新登录' }); } }); module.exports = router;
四、关键注意事项
- 安全优先:Refresh Token一定要存在服务端的安全存储里(比如Redis),绝对不能在前端暴露;返回Refresh Token时用HttpOnly、Secure的Cookie,避免XSS攻击。
- 令牌旋转:每次刷新时生成新的Refresh Token,旧的立即失效,能有效降低Refresh Token被盗用后的风险。
- 重试控制:拦截器里一定要加
_retry标记,防止因为刷新失败导致无限循环请求。 - 错误处理:刷新失败后要及时清理用户的服务端会话,并重定向到登录页,避免用户停留在无权限状态。
内容来源于stack exchange
相关产品推荐
相关产品推荐

