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

使用useSession钩子在客户端获取session时为何返回undefined?

登录页Session获取异常与页面保护优化

问题描述

需要实现页面保护逻辑:已登录用户访问/login页面时自动跳转到首页,但在login page.tsx中使用useSession钩子获取的session始终为undefined,而首页调用的UserSession组件却能正常获取session。

问题代码

login page.tsx

"use client";
import React, { FormEvent, useEffect } from "react";
import { signIn, useSession } from "next-auth/react";
import { useRouter } from "next/navigation";

const page = () => {

  const {data: session, status} = useSession();
  const router = useRouter();

  useEffect(() => {
    console.log(session);
    if (status === 'authenticated') {
      router.push('/')
    }
  },[])
  
  async function loggUser(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const form = new FormData(e.currentTarget);
    const email = form.get("email");
    const password = form.get("password");
    const data = { email, password };

    signIn('credentials',{...data, redirect: true, callbackUrl:"/"}).then((callback) => {
      if(callback?.error){
        alert(callback.error)
      }
      if(callback?.ok){
        alert("User logged in successful");
      }
    })
  }

  return (
    <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
      <div className="sm:mx-auto sm:w-full sm:max-w-sm">
        <img
          alt="Your Company"
          src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
          className="mx-auto h-10 w-auto"
        />
        <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
          Sign in to your account
        </h2>
      </div>

      <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
        <form onSubmit={loggUser} className="space-y-6">
          <div>
            <label
              htmlFor="email"
              className="block text-sm font-medium leading-6 text-gray-900"
            >
              Email address
            </label>
            <div className="mt-2">
              <input
                id="email"
                name="email"
                type="email"
                required
                autoComplete="email"
                className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
              />
            </div>
          </div>

          <div>
            <div className="flex items-center justify-between">
              <label
                htmlFor="password"
                className="block text-sm font-medium leading-6 text-gray-900"
              >
                Password
              </label>
              <div className="text-sm">
                <a
                  href="#"
                  className="font-semibold text-indigo-600 hover:text-indigo-500"
                >
                  Forgot password?
                </a>
              </div>
            </div>
            <div className="mt-2">
              <input
                id="password"
                name="password"
                type="password"
                required
                autoComplete="current-password"
                className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
              />
            </div>
          </div>

          <div>
            <button
              type="submit"
              className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
            >
              Sign in
            </button>
          </div>
        </form>

        <div className="w-full min-h-9 mt-10 relative">
          <div className="absolute top-[50%] w-full h-[1px] bg-slate-300 "></div>
          <div className="bg-white flex justify-center items-center py-3 px-5 absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]">
            <p>Or continue with</p>
          </div>
        </div>

        <div className="w-full flex justify-center items-center mt-5">
          <button onClick={async () => await signIn('google',{redirect: true, callbackUrl:"/"})}>
            <div className="flex justify-center items-center gap-3 border-slate-200 border-[1px] px-16 py-1 rounded-lg">
              <img
                className="w-8 h-8"
                src="https://cdn-icons-png.freepik.com/256/13170/13170545.png?semt=ais_hybrid"
                alt="Google"
              />
              <p>Google</p>
            </div>
          </button>
        </div>

        <p className="mt-10 text-center text-sm text-gray-500">
          Not a member?{" "}
          <a
            href="#"
            className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
          >
            Start a 14 day free trial
          </a>
        </p>
      </div>
    </div>
  );
};

export default page;

UserSession组件

"use client";
import React from "react";
import { useSession } from "next-auth/react";

const UserSession = () => {
  const { data: session,status } = useSession();
  console.log(status);
  
  return (
    <div>
      <p>{session == null ? "not logged in" : "logged in"}</p>
      <p>{JSON.stringify(session)} --- {status}</p>
    </div>
  );
};

export default UserSession;

问题原因

  1. useEffect依赖缺失:useEffect未监听session和status的变化,组件挂载时useSession处于loading状态,此时session为undefined,后续状态更新后useEffect不会重新执行,无法检测到authenticated状态。
  2. 页面保护方式有缺陷:仅用客户端组件做页面保护存在视觉延迟,用户可能短暂看到登录页内容后才跳转,且安全性不如服务端检查。

解决方案

方案1:修复客户端页面保护逻辑

修改login page.tsx中的useEffect,添加session、status和router作为依赖项,确保状态变化时重新执行跳转逻辑:

useEffect(() => {
  console.log(session);
  if (status === 'authenticated') {
    router.push('/')
  }
}, [session, status, router])

方案2:服务端前置检查(推荐)

将/login页面改为服务端组件,提前在服务端检查session,避免客户端渲染的延迟和内容泄露:

// app/login/page.tsx
import { getServerSession } from "next-auth/next";
import { authOptions } from "../api/auth/[...nextauth]/route"; // 替换为你的auth配置路径
import LoginClient from "./LoginClient"; // 将原登录UI拆分为客户端组件
import { redirect } from "next/navigation";

export default async function LoginPage() {
  const session = await getServerSession(authOptions);
  
  // 已登录则直接服务端重定向
  if (session) {
    redirect("/");
  }

  return <LoginClient />;
}

原登录逻辑和UI部分迁移到LoginClient.tsx客户端组件中,这样用户访问登录页时,服务端会先完成session检查,直接重定向到首页,不会加载登录页内容。

补充说明

  • useSession在客户端组件中是异步获取session的,初始状态为loading,必须等待状态变为authenticated或unauthenticated后再执行逻辑。
  • 服务端检查session是更安全的实现方式,能避免客户端渲染带来的内容泄露风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 18:45:55