5.使用 NextAuth 和 GoogleProvider 的会话管理

这是使用 GoogleProvider 进行 NextAuth 基本配置的第二部分。本章的最终代码可以在 GitHub 上找到(分支:basicgoogleprovider)。

useSession 实际应用

我们将 useSession hook 添加到我们的 <SignInButton /> 中,因为它已经是一个客户端组件。我们将会话信息记录到控制台中:

'use client';

import { signIn, useSession } from 'next-auth/react';

export default function SignInButton() {
  const { data: session, status, update } = useSession();
  console.log('session', session);
  return (
    <button
      className='bg-sky-400 rounded-md px-4 py-2'
      onClick={() => signIn()}
    >
      sign in
    </button>
  );
}

在浏览器控制台中,我们会看到如下输出:

{
  "user": {
    "name": "Peter Jacxsens",
    "email": "email",
    "image": "imagelink"
  },
  "expires": "2024-04-06T16:03:44.887Z"
}

这些信息来自哪里?这是 GoogleProvider 的默认 NextAuth 设置。当我们进行登录过程时,NextAuth 与 Google 进行了通信并获取了上述用户数据。NextAuth 随后将这些数据放入 JWT token 中,并写入一个 cookie。useSession 读取这个 cookie,获取 JWT token,从中提取数据并返回。这就是我们刚刚在浏览器中记录的内容。

请注意,会话信息来自我们 cookie 中的 token。NextAuth 并不会每次都去 Google 获取这些信息。

getServerSession 实际应用

我们在构建什么?当用户未登录时,我们希望显示登录按钮。但当用户已登录时,我们希望显示用户名和登出按钮。

image.png

image.png

首先,回到 <SignInButton /> 并移除 useSession hook 和 console.log

'use client';

import { signIn } from 'next-auth/react';

export default function SignInButton() {
  return (
    <button
      className='bg-sky-400 rounded-md px-4 py-2'
      onClick={() => signIn()}
    >
      sign in
    </button>
  );
}

将此文件复制一份,重命名为 SignOutButton,并从 NextAuth 导入 signOut

'use client';

import { signOut } from 'next-auth/react';

export default function SignOutButton() {
  return (
    <button
      className='bg-sky-400 rounded-md px-4 py-2'
      onClick={() => signOut()}
    >
      sign out
    </button>
  );
}

这应该很简单,我们制作了一个登出按钮,而 NextAuth 提供了 signOut 函数。

创建一个新的组件 <NavbarUser />,这是一个使用 getServerSession 函数的异步服务器组件:

import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/authOptions';
import SignInButton from './SignInButton';
import SignOutButton from './SignOutButton';

export default async function NavbarUser() {
  const session = await getServerSession(authOptions);
  console.log('session', session);

  if (!session) {
    return <SignInButton />;
  }
  return (
    <>
      <div className='text-sky-700'>{session.user?.name}</div>
      <SignOutButton />
    </>
  );
}

再次强调,这很简单。我们调用 getServerSession 函数并传入 authOptions 对象。我们记录 session,然后使用它来显示登录或未登录的 UI。

最后,在 <Navbar /> 中替换 <SignInButton /><NavbarUser />,我们看看 session。由于这是服务器会话,它只会在终端中记录,而不会在我们的浏览器中显示:

getServerSession {
  "user": {
    "name": "Peter Jacxsens",
    "email": "email",
    "image": "imagelink"
  }
}

我们获得了用户对象,但没有过期日期。这是有原因的,但我们不会在本系列中详细探讨。

身份验证流程

现在是测试时间。我们点击登出,页面完全重新加载,然后只显示登录按钮。我们的终端现在显示:

getServerSession: null

我们点击登录按钮,页面再次完全重新加载,跳转到默认的登录页面(那个不太好看的)。点击 "Sign in with Google" 按钮。这次 Google 不再询问我们权限,而是直接登录并重定向到我们的首页,页面再次完全重新加载。我们的终端再次记录了用户信息。

这就是全部。我们已经实现了完整的登录和登出功能,并且知道如何使用客户端组件 useSession hook 或服务器端 getServerSession 函数来检查用户是否已登录。

客户端或服务器组件

此时,你可能会想,什么时候使用 useSession,什么时候使用 getServerSession?其实很简单,我们使用与 Next 一样的原则:除非你需要客户端组件,否则总是使用服务器组件。

什么时候需要客户端组件?当你需要以下功能时:

  • 交互性或事件监听器(例如,按钮)
  • Hooks
  • 仅限浏览器的 API,如 localstoragegeolocation
  • 类组件

会话过期

NextAuth 的会话有 30 天的有效期。当会话过期时,你需要重新登录。会话的有效期可以在 NextAuth 设置中配置。NextAuth 和 Strapi 都建议 JWT token 的最大有效期为 30 天。

一些额外的组件

我们来进行一些清理。首先,移除 <NavbarUser /> 组件中的 console.log。接下来,我们将添加两个组件,一个使用 useSession,一个使用 getServerSession

我们将使用这两个组件来:

  • 清晰地展示登录状态。
  • 通过这些组件记录会话信息,便于访问会话。
  • 处理后续可能遇到的一些服务器和客户端组件问题。
// frontend/src/components/loggedIn/LoggedInClient.tsx

'use client';

import { useSession } from 'next-auth/react';

export default function LoggedInClient() {
  const { data: session } = useSession();
  console.log('useSession', session);
  return (
    <div
      className={`p-4 basis-2/4 rounded-sm text-center ${
        session ? 'bg-green-400' : 'bg-red-400'
      }`}
    >
      Client:{' '}
      {session ? `logged in as ${session.user?.name}.` : 'not logged in.'}
    </div>
  );
}
// frontend/src/components/loggedIn/LoggedInServer.tsx

import { authOptions } from '@/app/api/auth/[...nextauth]/authOptions';
import { getServerSession } from 'next-auth';

export default async function LoggedInServer() {
  const session = await getServerSession(authOptions);
  console.log('getServerSession', session);
  return (
    <div
      className={`p-4 basis-2/4 rounded-sm text-center ${
        session ? 'bg-green-400' : 'bg-red-400'
      }`}
    >
      Server:{' '}
      {session ? `logged in as ${session.user?.name}.` : 'not logged in.'}
    </div>
  );
}

将这些组件添加到根布局中,你将看到以下内容:(显然,登录时为绿色)

image.png

总结

我们在 NextAuth 中配置了 GoogleProvider,并添加了一个登录按钮。这使我们可以跳转到一个默认的 NextAuth 页面,该页面在实际应用中并不实用,但确实可以让我们使用 Google 登录。

useSession hook 和 getServerSession 函数让我们可以验证是否有用户登录。当用户登录时,它们会返回一个包含用户名、邮箱和头像属性的用户对象。当用户未登录时,它们返回 null。我们使用这些信息并相应地更改 UI。

但也有一些问题:

  • 我们确实需要一个自定义登录页面。另外,为什么每次都会重新加载?
  • 我们还没有与 Strapi 集成。目前所有操作都是前端的,用户信息不会进入我们的数据库。

我们将在接下来的章节中解决这些问题。

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68k 声望104.9k 粉丝