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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 12:23:10