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

NextJS客户端请求自动添加Access Token失效及Token刷新问题排查

问题排查与解决方案

一、客户端请求无法自动附加Token的问题

1. 检查Cookie的HttpOnly属性

登录时用nookies.set存储Token时,如果设置了HttpOnly: true,客户端JS无法读取该Cookie,拦截器自然取不到Token。服务端能正常获取是因为服务端不受HttpOnly限制。

  • 错误示例(客户端无法读取):
    nookies.set(ctx, 'accessToken', token, {
      httpOnly: true,
      path: '/',
      maxAge: 3600
    })
    
  • 修正方案(移除HttpOnly,配合安全属性降低风险):
    nookies.set(ctx, 'accessToken', token, {
      httpOnly: false,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      path: '/',
      maxAge: 3600
    })
    

2. 客户端正确读取Cookie的写法

nookies在客户端和服务端的API有差异,客户端读取Cookie需传入null作为第一个参数:

import nookies from 'nookies'
import axios from 'axios'

const axiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL })

// 客户端请求拦截器
axiosInstance.interceptors.request.use(config => {
  const cookies = nookies.get(null)
  const accessToken = cookies.accessToken
  if (accessToken) {
    config.headers.Authorization = `Bearer ${accessToken}`
  }
  return config
})

3. 确保拦截器全局生效

要保证Axios实例全局复用,且拦截器在客户端环境初始化(比如在_app.tsx中完成初始化,避免仅在服务端代码中配置):

// pages/_app.tsx
import axiosInstance from '../utils/axios'

function MyApp({ Component, pageProps }) {
  // 确保拦截器在客户端初始化
  useEffect(() => {
    // 若拦截器已在axios实例文件中初始化,此步可省略
  }, [])
  return <Component {...pageProps} />
}

二、Token刷新逻辑异常的问题

1. 避免刷新请求进入循环

刷新Token的请求本身不需要携带旧Access Token,否则会触发拦截器重复刷新,导致循环:

axiosInstance.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config
    // 仅处理403且未尝试过刷新的请求
    if (error.response.status === 403 && !originalRequest._retry) {
      originalRequest._retry = true
      try {
        // 刷新请求不携带Authorization头,开启withCredentials传递Refresh Token的Cookie
        const refreshRes = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/token/refresh`, {}, {
          withCredentials: true
        })
        const newToken = refreshRes.data.accessToken
        // 更新Cookie中的Token
        nookies.set(null, 'accessToken', newToken, {
          httpOnly: false,
          secure: process.env.NODE_ENV === 'production',
          sameSite: 'strict',
          path: '/',
          maxAge: 3600
        })
        // 更新原请求头并重发
        originalRequest.headers.Authorization = `Bearer ${newToken}`
        return axiosInstance(originalRequest)
      } catch (refreshErr) {
        // 刷新失败跳转登录页
        window.location.href = '/login'
        return Promise.reject(refreshErr)
      }
    }
    return Promise.reject(error)
  }
)

2. 确认Refresh Token的传递逻辑

如果Refresh Token存在Cookie中,必须开启withCredentials: true让浏览器自动携带;如果存在localStorage或其他存储,需手动将其加入请求头或请求体。

3. 检查后端刷新接口逻辑

确认后端/token/refresh接口能正确识别Refresh Token(从Cookie或请求参数中获取),返回有效的新Access Token,同时处理Refresh Token过期的场景。


内容的提问来源于stack exchange,提问作者kd12345

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:13:33