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

使用Axios拦截器时NextAuth Token显示Undefined的问题排查

问题:NextAuth + Axios拦截器中Authorization头Bearer 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

问题原因分析

  1. Session初始化时序问题:useSession()返回的session在组件挂载初期可能未完全加载,此时session?.user.token为undefined,拦截器会直接将Bearer undefined写入请求头。
  2. 拦截器逻辑漏洞:原代码仅在Authorization头不存在时设置,但未处理session为null或token缺失的情况,导致无效请求头被保留。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:13:18