使用Axios拦截器时NextAuth Token显示Undefined的问题排查
我正在使用NextAuth和Axios拦截器实现登录API,会话功能运行正常。控制台能获取到Token,但检查Authorization请求头时,Bearer Token显示为Undefined。
现有实现代码
[...nextauth.js] 文件
import NextAuth, { getServerSession } from 'next-auth/next' import CredentialsProvider from 'next-auth/providers/credentials' import axios from 'src/lib/axios' const authOptions = { providers: [ CredentialsProvider({ async authorize(credentials, req) { const { email, password } = credentials const res = await axios.post(`/login`, { email: email, password: password }) const user = res.data if (user) { return user } else { return null } } }) ], callbacks: { async jwt({ token, user, account }) { return { ...token, ...user, ...account } }, async session({ session, token, user }) { session.user = token return session } } } export default NextAuth(authOptions)
axios.js 文件
import axios from 'axios' export const axiosAuth = axios.create({ baseURL: `${process.env.NEXT_PUBLIC_SERVER_URL}/api`, withCredentials: true, headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json, multipart/form-data' } })
useAxios.js 文件
import { useSession } from 'next-auth/react' import { useEffect } from 'react' import { axiosAuth } from './axios' const useAxiosAuth = () => { const { data: session } = useSession() useEffect(() => { const requestIntercept = axiosAuth.interceptors.request.use(config => { if (!config.headers['Authorization']) { config.headers['Authorization'] = `Bearer ${session?.user.token}` } return config }) return () => { axiosAuth.interceptors.request.eject(requestIntercept) } }, [session?.user.token]) return axiosAuth } export default useAxiosAuth
问题原因分析
- Session初始化时序问题:
useSession()返回的session在组件挂载初期可能未完全加载,此时session?.user.token为undefined,拦截器会直接将Bearer undefined写入请求头。 - 拦截器逻辑漏洞:原代码仅在
Authorization头不存在时设置,但未处理session为null或token缺失的情况,导致无效请求头被保留。 - JWT回调合并逻辑风险:直接合并
token、user、account可能导致字段覆盖或结构混乱,若user中无token字段,会导致session.user.token不存在。
修复方案
1. 修正Axios拦截器逻辑
更新useAxios.js,确保仅在有效token存在时设置请求头,同时清理无效情况:
import { useSession } from 'next-auth/react' import { useEffect } from 'react' import { axiosAuth } from './axios' const useAxiosAuth = () => { const { data: session } = useSession() useEffect(() => { // 清除已有拦截器,避免重复添加 axiosAuth.interceptors.request.clear() const requestIntercept = axiosAuth.interceptors.request.use(config => { if (session?.user?.token) { config.headers['Authorization'] = `Bearer ${session.user.token}` } else { // 无有效token时移除Authorization头 delete config.headers['Authorization'] } return config }) return () => { axiosAuth.interceptors.request.eject(requestIntercept) } }, [session]) return axiosAuth } export default useAxiosAuth
2. 优化JWT回调逻辑
在[...nextauth.js]中确保仅在user存在时合并字段,避免结构混乱:
callbacks: { async jwt({ token, user }) { // 仅当登录成功返回user时,合并用户数据到token if (user) { token = { ...token, ...user } } return token }, async session({ session, token }) { session.user = token return session } }
3. 验证后端返回结构
确认/login接口返回的user对象包含token字段,示例返回格式:
{ "id": 1, "email": "user@example.com", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }
4. 确保组件等待Session加载完成
在使用useAxiosAuth的组件中,等待会话加载完成后再发起请求:
import { useSession } from 'next-auth/react' import useAxiosAuth from '../lib/useAxios' const ProtectedComponent = () => { const { status } = useSession() const axiosAuth = useAxiosAuth() if (status === 'loading') { return <div>加载中...</div> } const fetchProtectedData = async () => { try { const response = await axiosAuth.get('/protected-api') console.log(response.data) } catch (error) { console.error('请求失败:', error) } } return ( <button onClick={fetchProtectedData}> 获取受保护数据 </button> ) } export default ProtectedComponent
内容的提问来源于stack exchange,提问作者Edmar Sanchez
相关产品推荐
相关产品推荐

