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
相关产品推荐
相关产品推荐

