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

如何持久化NextAuth用户会话?解决刷新页面用户数据丢失问题

问题:登录后刷新/home页面,用户数据变为null

我需要实现用户登录后存储返回的用户数据(包含用于其他路由获取数据的ID),登录成功后跳转至/home路由,使用会话中的ID获取数据。初始状态下一切正常,但刷新/home页面后,user变为null。

[...nextauth].js 代码

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import axios from "axios";

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: "credentials",

      credentials: {
        username: { label: "Username", type: "text", placeholder: "justin" },
        password: {label: "Password",type: "password",placeholder: "******"},
      },

      async authorize(credentials, req) {
        const url = req.body.callbackUrl.split("/auth")[0];
        const { username, password } = credentials;
        const user = await axios({
          url: `${url}/api/user/login`,
          method: "POST",
          data: {
            username: username,
            password: password,
          },
          "content-type": "application/json",
        })
          .then((res) => {
            return res.data;
          })
          .catch((err) => {
            if (err.response.data) {
              throw new Error(err.response.data);
            } else {
              return null;
            }
            return null;
          });
        return user;
      },
    }),
  ],

  callbacks: {
    jwt: ({ token, user }) => {
      if (user) {
        token.user = user;
      }

      return token;
    },
    session: ({ session, token }) => {
      if (token) {
        session.user = token.user;
      }

      return session;
    },
  },

  pages: {
    signIn: "/auth/login",
    newUser: "/auth/register",
  },
});

/home 路由代码

import Card from "@/components/card/Card";
import React, { useEffect, useState } from "react";
import styles from "./home.module.css";
import { Ubuntu } from "@next/font/google";
import { useSession } from "next-auth/react";
import { useDispatch, useSelector } from "react-redux";

const ubuntu = Ubuntu({ weight: "500", subsets: ["cyrillic"] });
const getData = async (id) => {
  const res = await fetch({
    url: "http://localhost:3000/api/note/getall",
    method: "POST",
    "content-type": "application/json",
    data: {
      id: id,
    },
  });
  if (!res.ok) {
    console.log(id);
    throw new Error("Unable  to fetch");
  } else {
    return res.json();
    console.log(res);
  }
};

function home() {
  const colors = ["#E9F5FC", "#FFF5E1", "#FFE9F3", "#F3F5F7"];
  const random = Math.floor(Math.random() * 5);
  const rc = colors[random];
  const [pop, setPop] = useState("none");
  const { user } = useSelector((state) => state.user);
  const getDataa = async () => {
    console.log(user)
    const data = await getData(user._id);
    console.log(data);
  };
  useEffect(() => {
    if (user) {
      alert(user)
    }
  }, []);
  return (
    <div className={styles.home}>
      <header>
        <h3 className={ubuntu.className}>
          Hello, <br /> {user?.username}!
        </h3>
        <input type="text" placeholder="search" />
      </header>

      <div className={styles.nav}>
        <h1 className={ubuntu.className}>Notes</h1>
      </div>

      <div className={styles.section}>
        <div className={styles.inner}>
          {/*           {data &&
            data.map((e) => (
              <Card
                rawData={e}
                color={colors[Math.floor(Math.random() * colors.length)]}
              />
            ))} */}
        </div>
      </div>

      <div className="new"></div>
    </div>
  );
}

export default home;

解决方案

问题根源

  1. Redux状态丢失:Redux是内存状态管理,页面刷新后状态会重置,直接从Redux取user会导致刷新后数据为空。
  2. 未正确依赖NextAuth会话:虽引入useSession但未实际使用,也未将会话数据同步到Redux。
  3. fetch用法错误:getData中fetch参数格式错误,会导致请求失败。

具体修复步骤

1. 修正Home组件,正确使用NextAuth会话

import Card from "@/components/card/Card";
import React, { useEffect, useState } from "react";
import styles from "./home.module.css";
import { Ubuntu } from "@next/font/google";
import { useSession } from "next-auth/react";
import { useDispatch, useSelector } from "react-redux";

const ubuntu = Ubuntu({ weight: "500", subsets: ["cyrillic"] });

// 修复fetch参数格式
const getData = async (id) => {
  const res = await fetch("http://localhost:3000/api/note/getall", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ id: id }),
  });
  if (!res.ok) {
    console.log(id);
    throw new Error("Unable to fetch");
  }
  return res.json();
};

function Home() {
  const colors = ["#E9F5FC", "#FFF5E1", "#FFE9F3", "#F3F5F7"];
  const [pop, setPop] = useState("none");
  const { data: session, status } = useSession();
  const dispatch = useDispatch();
  const reduxUser = useSelector((state) => state.user);

  // 会话加载完成后同步到Redux
  useEffect(() => {
    if (status === "authenticated" && session?.user) {
      dispatch({ type: "SET_USER", payload: session.user }); // 替换为你的Redux更新action
    }
  }, [session, status, dispatch]);

  const currentUser = reduxUser || session?.user;

  const getDataa = async () => {
    if (!currentUser) return;
    try {
      const data = await getData(currentUser._id);
      console.log(data);
    } catch (err) {
      console.error(err);
    }
  };

  useEffect(() => {
    if (currentUser) {
      getDataa();
    }
  }, [currentUser]);

  // 处理加载状态
  if (status === "loading") {
    return <div>加载中...</div>;
  }

  // 未登录跳转登录页
  if (status === "unauthenticated") {
    window.location.href = "/auth/login";
    return null;
  }

  return (
    <div className={styles.home}>
      <header>
        <h3 className={ubuntu.className}>
          Hello, <br /> {currentUser?.username}!
        </h3>
        <input type="text" placeholder="search" />
      </header>

      <div className={styles.nav}>
        <h1 className={ubuntu.className}>Notes</h1>
      </div>

      <div className={styles.section}>
        <div className={styles.inner}>
          {/*           {data &&
            data.map((e) => (
              <Card
                rawData={e}
                color={colors[Math.floor(Math.random() * colors.length)]}
              />
            ))} */}
        </div>
      </div>

      <div className="new"></div>
    </div>
  );
}

export default Home;

2. 完善NextAuth配置

在[...nextauth].js中添加会话密钥,确保JWT持久化:

import process from "process";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import axios from "axios";

export default NextAuth({
  secret: process.env.NEXTAUTH_SECRET, // 从.env.local获取,可用openssl rand -hex 32生成
  providers: [/* 现有代码 */],
  callbacks: [/* 现有代码 */],
  pages: [/* 现有代码 */],
});

在.env.local中添加NEXTAUTH_SECRET=生成的随机字符串。

3. 全局配置SessionProvider(可选)

在_app.js中添加会话提供者,确保全应用可访问会话:

import { SessionProvider } from "next-auth/react";

export default function App({ Component, pageProps: { session, ...pageProps } }) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

核心逻辑说明

  • 刷新页面后,NextAuth会从Cookie恢复会话,useSession会先进入loading状态,加载完成后返回用户数据。
  • 通过useEffect监听会话变化,将数据同步到Redux,保证状态一致性。
  • 修复fetch参数错误,使用标准的POST请求格式发送数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:55:35