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

NextJS 13.4中useEffect+Axios拦截器页面刷新401错误求助

问题定位

刷新页面触发401的核心原因是NextAuth客户端会话初始化存在延迟:

  • 页面刷新后,useSession会先返回status: "loading"、session: undefined的状态,此时组件的useEffect依赖axios(来自useAxiosAuth)立即执行请求
  • 拦截器里拿到的session是undefined,导致请求头的Authorization被设为Bearer undefined,被NestJS API拒绝
  • 按钮点击时,会话已经完成初始化,所以请求能正常携带token
解决方案

1. 组件层:等待会话就绪后再发起请求

修改Profile组件,通过useSession的status判断会话状态,仅当状态为authenticated时才执行请求:

"use client"

import React, { useEffect, useState } from "react"
import { useSession } from "next-auth/react"
import useAxiosAuth from "@/lib/hooks/useAxiosAuth"
import UserProfile from "@/types/user-profile"

export default function Profile() {
  const [userProfile, setUserProfile] = useState<UserProfile>()
  const axios = useAxiosAuth()
  const { status } = useSession()

  useEffect(() => {
    if (status !== "authenticated") return
    
    ;(async () => {
      try {
        const res = await axios.get<UserProfile>("/profile")
        setUserProfile(res.data)
      } catch (err) {
        console.error("获取用户信息失败:", err)
      }
    })()
  }, [axios, status])
  
  if (status === "loading") {
    return <div>加载中...</div>
  }

  if (status === "unauthenticated") {
    return <div>请先登录</div>
  }
  
  return (
    <>
      {/* 渲染用户信息 */}
    </>
  )
}

2. 拦截器层:增加session存在性校验

修改useAxiosAuth的请求拦截器,确保只有session存在时才设置Authorization头,避免发送无效请求:

"use client"

import { useEffect } from "react"
import { axiosAuth } from "lib/axios"
import { useSession } from "next-auth/react"
import { useRefreshToken } from "./useRefreshToken"

const useAxiosAuth = () => {
  const { data: session, status } = useSession()
  const refreshToken = useRefreshToken()

  useEffect(() => {
    const requestIntercept = axiosAuth.interceptors.request.use(
      (config) => {
        if (!session?.user) return config
        
        if (!config.headers["Authorization"]) {
          config.headers["Authorization"] = `Bearer ${session.user.accessToken}`
        }
        if (!config.headers["Token-Id"]) {
          config.headers["Token-Id"] = `${session.user.tokenId}`
        }
        return config
      },
      (error) => Promise.reject(error)
    )

    const responseIntercept = axiosAuth.interceptors.response.use(
      (response) => response,
      async (error) => {
        const prevRequest = error?.config
        if (!session?.user) {
          window.location.href = "/api/auth/signin"
          return Promise.reject(error)
        }
        
        if (error?.response?.status === 401 && !prevRequest?._retry) {
          prevRequest._retry = true

          await refreshToken()
          prevRequest.headers["Authorization"] = `Bearer ${session.user.accessToken}`

          return axiosAuth(prevRequest)
        }
        return Promise.reject(error)
      }
    )

    return () => {
      axiosAuth.interceptors.request.eject(requestIntercept)
      axiosAuth.interceptors.response.eject(responseIntercept)
    }
  }, [session, refreshToken, status])

  return axiosAuth
}

export default useAxiosAuth

3. 优化refreshToken逻辑:避免直接修改session对象

useRefreshToken里直接修改session.user的做法不符合React状态更新规范,改为仅通过update方法更新会话:

"use client"

import axios from "lib/axios"
import { signIn, useSession } from "next-auth/react"

export const useRefreshToken = () => {
  const { data: session, update, status } = useSession()
  const refreshToken = async () => {
    if (!session?.user) {
      signIn()
      return
    }
    
    try {
      const res = await axios.post(
        "/auth/refresh",
        {},
        {
          headers: {
            Authorization: `Bearer ${session.user.refreshToken}`,
            "Token-Id": session.user.tokenId,
          },
        }
      )

      await update({
        ...session,
        user: {
          ...session.user,
          accessToken: res.data.accessToken,
          refreshToken: res.data.refreshToken,
          tokenId: res.data.tokenId,
          accessTokenExpires: res.data.accessTokenExpires,
        },
      })
    } catch (err) {
      console.error("刷新token失败:", err)
      signIn()
    }
  }
  return refreshToken
}
关键说明
  • NextAuth的客户端会话是异步初始化的,必须通过status状态判断会话是否就绪,不能假设组件挂载时session就存在
  • 拦截器中增加session校验,避免发送无效请求,同时在401且session不存在时直接触发登录
  • 不要直接修改NextAuth的session对象,必须通过update方法更新,否则会导致状态不同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:55:09