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

Next-auth + RTK Query:请求返回401时如何跳转至登录页?

解决Next-auth + RTK Query 401未授权自动跳转登录页的方案

1. 全局捕获RTK Query的401错误

RTK Query支持自定义baseQuery来统一处理请求错误,这是捕获401的核心入口。你可以包装fetchBaseQuery,在错误回调里判断状态码并执行跳转:

// src/services/apiSlice.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { signOut } from 'next-auth/react';

const customBaseQuery = fetchBaseQuery({
  baseUrl: '/api',
  credentials: 'include', // 确保携带会话cookie
  prepareHeaders: (headers) => {
    // 从Next-auth会话中获取token并添加到请求头
    const session = JSON.parse(localStorage.getItem('next-auth.session'));
    if (session?.user?.token) {
      headers.set('authorization', `Bearer ${session.user.token}`);
    }
    return headers;
  },
});

const baseQueryWithReauth = async (args, api, extraOptions) => {
  let result = await customBaseQuery(args, api, extraOptions);

  // 捕获401错误并处理
  if (result.error?.status === 401) {
    // 先登出Next-auth会话
    await signOut({ redirect: false });
    // 跳转到登录页,携带当前页面作为回调地址
    window.location.href = `/api/auth/signin?callbackUrl=${encodeURIComponent(window.location.href)}`;
  }

  return result;
};

export const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: baseQueryWithReauth,
  endpoints: (builder) => ({
    // 你的业务接口定义
  }),
});

2. 在_app.js中监听会话状态变化

除了请求层面的401捕获,还要监听Next-auth的会话状态,当会话主动失效时直接跳转:

// pages/_app.js
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';

function MyApp({ Component, pageProps }) {
  const { status } = useSession();
  const router = useRouter();

  useEffect(() => {
    // 当会话变为未认证状态,且当前不在登录页时跳转
    if (status === 'unauthenticated' && router.pathname !== '/api/auth/signin') {
      router.push('/api/auth/signin');
    }
  }, [status, router]);

  return <Component {...pageProps} />;
}

export default MyApp;

3. 优化Next-auth的JWT配置(针对JWT认证场景)

你之前设置的maxAge:60是会话有效期,但还需要在jwt回调里处理token刷新,减少不必要的401:

// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';

export default NextAuth({
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        // 这里写你的登录验证逻辑,返回包含token和过期时间的用户对象
        const user = await yourAuthService.login(credentials);
        return user;
      },
    }),
  ],
  session: {
    strategy: 'jwt',
    maxAge: 60, // 会话有效期60秒
  },
  jwt: {
    async jwt({ token, user }) {
      // 首次登录时保存token信息
      if (user) {
        token.accessToken = user.accessToken;
        token.expiresAt = user.expiresAt;
        token.refreshToken = user.refreshToken;
      }

      // 提前1分钟检查token是否即将过期,触发刷新
      if (Date.now() < token.expiresAt - 60000) {
        return token;
      }

      // 执行token刷新逻辑
      try {
        const refreshedData = await yourAuthService.refreshToken(token.refreshToken);
        return {
          ...token,
          accessToken: refreshedData.accessToken,
          expiresAt: refreshedData.expiresAt,
        };
      } catch (err) {
        // 刷新失败,标记token错误,触发会话失效
        return { ...token, error: 'RefreshTokenFailed' };
      }
    },
    async session({ session, token }) {
      // 将token信息同步到会话对象
      session.user.token = token.accessToken;
      session.error = token.error;
      return session;
    },
  },
});

为什么之前的方案无效?

你设置的refetchInterval和refetchOnWindowFocus只是触发数据重新请求,但没有处理请求返回的401错误,所以即使请求失败也不会触发跳转。必须通过全局错误捕获+会话状态监听的组合,才能覆盖所有401场景(包括token过期、后端主动返回401、会话主动失效)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 13:05:36