NextAuth Session 更新
原文地址:https://mp.weixin.qq.com/s/Nu4st2UYlqmXcOBD1eIIHg
相较于之前的登录登出,本篇教程增加了 4 个文件。
项目结构src/
├── app/
│ ├── api/
│ │ └── auth/
│ │ └── […nextauth]/
│ │ └── route.ts
│ ├── client/
│ │ └── page.tsx
│ └── ui/
│ └── update_button.tsx
└── auth_wrapper.tsx
1. 服务器端渲染 & 客户端渲染
在继续之前,我们简单讨论一下 Next.js 14 中服务器端渲染(SSR)和客户端渲染(CSR)的区别。网上可以找到详细的比较,所以这里我只解释本教程所需的概念。
Next.js 14 默认使用服务器端渲染来显示网页组件,在组件代码顶部添加 "use client"
行可以让 Next.js 14 使用客户端渲染来显示组件。
在服务器端代码中添加 console.log("message")
会在运行 Next 应用的终端中显示消息,而在客户端代码中添加则会在浏览器控制台显示消息(浏览器 -> 开发者工具 -> 控制台)。
现在我们在 src/app/page.tsx
中添加 console.log
,并编写 src/app/client/page.tsx
的代码:
src/app/page.tsxexport default async function Home() {
console.log('Server Side Rendering') // 添加console.log
return (
...
)
}
src/app/client/page.tsx"use client"
export default function Page() {
console.log('Client Side Rendering')
return (
<div>
<h1>Client Side Rendeing Page</h1>
<h2>Unavailable without auth</h2>
</div>
)
}
2. Session(会话)
之所以讨论 SSR 和 CSR,是因为 session 是的调用方法取决于代码是在服务器端还是客户端运行。
3. 在服务器端调用session: await auth()
src/auth.ts// 添加了 { handlers: { GET, POST }, auth, update }
export const { handlers: { GET, POST }, auth, signIn, signOut, update } = NextAuth({
...authConfig,
...
})
src/app/page.tsx |
---|
| mport { auth, signOut } from "@/auth" // 添加 { auth }
export default async function Home() { // 现在是'async function'
console.log('Server Side Rendering')
const session = await auth() // 调用session
console.log(session); // console log读取session
return (
...
)
}
|
4. 在客户端调用session: useSession()
要在客户端调用 session,我们可以使用 useSession()
钩子。要使用 useSession()
钩子,必须先完成几件事。我们必须设置一个内部 API 并用SessionProvider
包装应用。
src/app/api/auth/[...nextauth]/route.ts |
---|
| export { GET, POST } from "@/auth"
export const runtime = "edge"
|
src/app/auth_wrapper.tsx |
---|
| "use client";
// SessionProvider必须与客户端渲染一起使用
// 因此我们创建一个单独的客户端组件来运行AuthWrapper
import { SessionProvider } from "next-auth/react";
type Props = {
children:React.ReactNode;
}
export default function AuthWrapper({ children }: Props) {
return <SessionProvider>{children}</SessionProvider>;
}
|
src/app/layout.tsx |
---|
| import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import AuthWrapper from '../auth_wrapper'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<AuthWrapper> {/* 用AuthWrapper包装整个应用 */}
{children}
</AuthWrapper>
</body>
</html>
)
}
|
layout.tsx
文件是一个特殊文件,它作为 page.tsx 组件的父组件。现在让我们编辑 src/app/client/page.tsx
:
src/app/client/page.tsx |
---|
| "use client"
import { useSession } from "next-auth/react" // 添加import
export default function Page() {
console.log('Client Side Rendering')
const { data: session, update } = useSession() // useSession()
console.log(session); // console.log
return (
<div>
<h1>Client Side Rendeing Page</h1>
<h2>Unavailable without auth</h2>
</div>
)
}
|
5. Session更新
在 Next Auth v5 发布之前,更新 session 只能在客户端完成。有了新的 v5,session 更新也可以在服务器端完成。我们首先编辑 src/auth.ts
文件,在callbacks -> jwt
中添加了一个代码块。
src/auth.ts |
---|
| import NextAuth from 'next-auth';
import { authConfig } from './auth.config';
import Credentials from 'next-auth/providers/credentials';
import { User } from '@/lib/definitions';
export const { handlers: { GET, POST }, auth, signIn, signOut, update } = NextAuth({
...authConfig,
providers: [
Credentials({
async authorize(credentials) {
if (credentials.id && credentials.password) {
// 在这里添加你的后端代码
// let loginRes = await backendLogin(credentials.id, credentials.password)
let loginRes = {
success : true,
data : {
user: {
ID: "john_doe",
NAME: "John Doe",
EMAIL: "email@email.email",
},
}
}
// 登录失败
if (!loginRes.success) return null;
// 登录成功
const user = {
id: loginRes.data.user.ID ?? '',
name: loginRes.data.user.NAME ?? '',
email: loginRes.data.user.EMAIL ?? '',
} as User;
return user;
}
return null;
},
})
],
callbacks: {
async session({ session, token, user }) {
session.user = token.user as User
return session;
},
async jwt({ token, user, trigger, session }) {
if (user) {
token.user = user;
}
// ***************************************************************
// 添加的代码
if (trigger === "update" && session) {
token = {...token, user : session}
return token;
};
// **************************************************************
return token;
},
},
});
|
5.1 客户端Session更新
让我们先处理客户端代码,编辑 src/app/client/page.tsx
中的代码:
src/app/client/page.tsx |
---|
| "use client"
import { User } from "@/lib/definitions"
import { useSession } from "next-auth/react"
import { useEffect, useState } from "react"
export default function Page() {
const { data: session, update } = useSession()
// useState和useEffect用于仅在检索到session后
// 渲染UI
const [user, setUser] = useState<User>({} as User)
useEffect(() => {
if(session && session.user)
{
setUser(session.user as User)
console.log(session.user)
}
}, [session])
return (
session && // UI在检索到session后渲染
<div>
<h1>Client Side Rendeing Page</h1>
<h2>Unavailable without auth</h2>
<br />
<button onClick={ () => {
// 使用从useSession钩子调用的update()
update({...user, name: 'Client-Man'});
}}>
Client Side Update
</button>
</div>
)
}
|
访问 http://localhost:3000/client ,打开浏览器控制台,然后点击 "Client Side Update" 按钮。控制台将打印更新后的 session 用户数据。
5.2 服务器Session更新
在服务器端更新 session 有两种方式。第一种方式是在服务器组件中调用客户端组件来运行客户端 Session 更新,另一种方式是利用 NextAuth v5 的新特性;在服务器组件中使用从 @auth
导入的 update
方法。
5.2.1 服务器组件中的客户端组件
首先,我们在 src/ui/update_button.tsx
中创建一个单独的客户端组件文件。
src/ui/update_button.tsx |
---|
| "use client"
import { useSession } from "next-auth/react"
export default function UpdateButton({newName} : {newName: String}) {
const { data: session, update } = useSession()
return (
<button onClick={() => {
update({...session!.user, name: newName});
}}>
Client Side Update
</button>
)
}
|
编辑 src/app/page.tsx 文件
src/app/page.tsx |
---|
| import { auth, signOut } from "@/auth"
import UpdateButton from "@/ui/update_button";
export default async function Home() {
console.log('Server Side Rendering')
const session = await auth() // 调用session
console.log(session); // console log读取session
return (
<div>
<h1>Home Page</h1>
<h2>Unavailable without auth</h2>
<form
action={async () => {
'use server';
await signOut();
}}
>
<button>
Log Out
</button>
<br />
<UpdateButton newName={'Server-Man'} />
</form>
</div>
)
}
|
访问 http://localhost:3000/ ,然后点击'Client Component Update'按钮。刷新页面或转到不同的路由(http://localhost:3000/client)。
5.2.2 在服务器组件中使用从@auth导入的update
编辑 src/app/page.tsx 文件:
src/app/page.tsx |
---|
| import { auth, signOut, update } from "@/auth"
import UpdateButton from "@/ui/update_button";
export default async function Home() {
console.log('Server Side Rendering')
const session = await auth() // 调用session
const user = session!.user;
console.log(session); // console log读取session
return (
<div>
<h1>Home Page</h1>
<h2>Unavailable without auth</h2>
<form
action={async () => {
'use server';
await signOut();
}}
>
<button>
Log Out
</button>
<br />
<UpdateButton newName={'Server-Man'} />
</form>
<br />
<form
action={async () => {
'use server';
await update({...user, name: 'Serverserver-man'});
}}
>
<button>
Server Side Update
</button>
</form>
</div>
)
}
|
访问 http://localhost:3000/ ,然后点击 Server Side Update 按钮。刷新页面或转到不同的路由(http://localhost:3000/client)。
我们可以检查 session 中的 user.name 已成功更新为'Serverserver-man'
我注意到这个新的 update
功能目前似乎有点不稳定。因此,在 NextAuth v5 正式发布之前,使用客户端组件更新方法将是一个更稳定的选择。