Next.js 14中使用Next-Auth实现Axios刷新令牌拦截器的问题
在Next.js 14中结合Next-Auth实现Axios刷新令牌拦截器
核心问题解决思路
useSession是React钩子,仅能在客户端组件的渲染周期内使用,无法直接在Axios拦截器这类非组件代码中调用。解决方案是使用Next-Auth提供的客户端异步函数getSession获取会话令牌,同时配合JWT会话策略实现令牌的更新与持久化。
步骤1:配置Next-Auth的JWT会话策略
确保你的[...nextauth].ts(App Router对应路径为app/api/auth/[...nextauth]/route.ts)配置为JWT策略,以便存储和更新访问/刷新令牌:
import NextAuth from 'next-auth'; import CredentialsProvider from 'next-auth/providers/credentials'; export const authOptions = { session: { strategy: 'jwt', // 必须使用JWT策略才能自定义令牌存储 maxAge: 3600, // 会话有效期,可根据令牌过期时间调整 }, providers: [ CredentialsProvider({ credentials: { email: { label: '邮箱', type: 'email' }, password: { label: '密码', type: 'password' }, }, async authorize(credentials) { // 替换为你的登录接口调用逻辑 const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/login`, { method: 'POST', body: JSON.stringify(credentials), headers: { 'Content-Type': 'application/json' }, }); const user = await res.json(); if (res.ok && user) { // 返回包含令牌的用户对象 return { id: user.id, email: user.email, accessToken: user.accessToken, refreshToken: user.refreshToken, }; } return null; }, }), ], callbacks: { async jwt({ token, user }) { // 登录时初始化令牌到JWT中 if (user) { token.accessToken = user.accessToken; token.refreshToken = user.refreshToken; } return token; }, async session({ session, token }) { // 将令牌注入会话,供客户端获取 session.user.accessToken = token.accessToken as string; session.user.refreshToken = token.refreshToken as string; return session; }, }, secret: process.env.NEXTAUTH_SECRET, }; export default NextAuth(authOptions);
步骤2:创建带拦截器的Axios实例
在lib/axios.ts中创建Axios实例,使用getSession获取令牌,并实现过期自动刷新逻辑:
import axios from 'axios'; import { getSession } from 'next-auth/react'; const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { 'Content-Type': 'application/json', }, }); // 请求拦截器:自动添加访问令牌 api.interceptors.request.use(async (config) => { const session = await getSession(); if (session?.user.accessToken) { config.headers.Authorization = `Bearer ${session.user.accessToken}`; } return config; }); // 响应拦截器:处理令牌过期并自动刷新 api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; // 仅处理401未授权错误,且避免重复重试 if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; const session = await getSession(); if (session?.user.refreshToken) { try { // 调用刷新令牌的API路由 const refreshRes = await axios.post('/api/auth/refresh-token', { refreshToken: session.user.refreshToken, }); const { accessToken: newAccessToken, refreshToken: newRefreshToken } = refreshRes.data; // 更新Next-Auth会话中的令牌 await fetch('/api/auth/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ accessToken: newAccessToken, refreshToken: newRefreshToken, }), }); // 更新原请求的令牌并重试 originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; return api(originalRequest); } catch (refreshError) { // 刷新失败,跳转至登录页 window.location.href = '/auth/login'; return Promise.reject(refreshError); } } } return Promise.reject(error); } ); export default api;
步骤3:创建刷新令牌的API路由
在pages/api/auth/refresh-token.ts(App Router对应路径为app/api/auth/refresh-token/route.ts)中实现令牌刷新逻辑,确保后端验证刷新令牌并返回新令牌:
// Pages Router版本 import { NextApiRequest, NextApiResponse } from 'next'; import { getToken } from 'next-auth/jwt'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'POST') { return res.status(405).json({ message: '仅支持POST请求' }); } const { refreshToken } = req.body; if (!refreshToken) { return res.status(400).json({ message: '缺少刷新令牌' }); } // 调用你的后端刷新令牌接口 const backendRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/refresh-token`, { method: 'POST', body: JSON.stringify({ refreshToken }), headers: { 'Content-Type': 'application/json' }, }); if (!backendRes.ok) { return res.status(401).json({ message: '刷新令牌无效' }); } const newTokens = await backendRes.json(); // 更新Next-Auth的JWT令牌 const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET }); if (token) { token.accessToken = newTokens.accessToken; token.refreshToken = newTokens.refreshToken; // Next-Auth会自动处理JWT的更新,无需手动存储 } res.status(200).json(newTokens); }
Next.js 14兼容性说明
Next.js 14本身与Next-Auth v4.23+版本不存在核心兼容性问题。若出现异常,大概率是以下原因:
- 未将会话策略设置为
jwt:默认的database策略无法灵活更新令牌 - Axios实例代码被导入到服务器组件中:
getSession是客户端函数,只能在客户端环境运行,需在客户端组件(添加'use client'指令)中导入使用Axios实例 - Next-Auth版本过旧:升级到最新的v4版本即可解决App Router相关适配问题
使用注意事项
- 避免在服务器组件中直接使用该Axios实例,需在客户端组件(添加
'use client')中导入 - 刷新令牌的后端接口需做好权限验证,防止恶意使用
- 可根据实际业务调整令牌过期的判断逻辑(比如解析JWT的exp字段提前刷新)
内容的提问来源于stack exchange,提问作者Jonathan Oliveira Bergamo
相关产品推荐
相关产品推荐

