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
实际应用
我们在构建什么?当用户未登录时,我们希望显示登录按钮。但当用户已登录时,我们希望显示用户名和登出按钮。
首先,回到 <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,如 localstorage
或geolocation
类组件
会话过期
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>
);
}
将这些组件添加到根布局中,你将看到以下内容:(显然,登录时为绿色)
总结
我们在 NextAuth 中配置了 GoogleProvider,并添加了一个登录按钮。这使我们可以跳转到一个默认的 NextAuth 页面,该页面在实际应用中并不实用,但确实可以让我们使用 Google 登录。
useSession
hook 和 getServerSession
函数让我们可以验证是否有用户登录。当用户登录时,它们会返回一个包含用户名、邮箱和头像属性的用户对象。当用户未登录时,它们返回 null
。我们使用这些信息并相应地更改 UI。
但也有一些问题:
我们确实需要一个自定义登录页面。另外,为什么每次都会重新加载? 我们还没有与 Strapi 集成。目前所有操作都是前端的,用户信息不会进入我们的数据库。
我们将在接下来的章节中解决这些问题。