认证

为 Next.js 配置服务端认证


Next.js 提供了两种模式:App RouterPages Router。您可以使用其中任何一种策略来设置服务器端认证。甚至可以在同一个应用中同时使用两者。

1

安装 Supabase 包

安装 @supabase/supabase-js 包和辅助包 @supabase/ssr

1
npm install @supabase/supabase-js @supabase/ssr
2

设置环境变量

在项目根目录下创建 .env.local 文件。

填写您的 NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEY

Project URL
Anon key
1
2
NEXT_PUBLIC_SUPABASE_URL=<your_supabase_project_url>NEXT_PUBLIC_SUPABASE_ANON_KEY=<your_supabase_anon_key>
3

编写创建 Supabase 客户端的工具函数

要从 Next.js 应用访问 Supabase,您需要两种类型的 Supabase 客户端:

  1. 客户端组件客户端 - 用于在浏览器中运行的 Client Components 中访问 Supabase。
  2. 服务端组件客户端 - 用于在仅服务器端运行的 Server Components、Server Actions 和 Route Handlers 中访问 Supabase。

创建一个 utils/supabase 文件夹,为每种客户端类型创建一个文件。然后复制每种客户端类型的工具函数。

1
2
3
4
5
6
7
8
import { createBrowserClient } from '@supabase/ssr'export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! )}
4

连接中间件

在项目根目录下创建 middleware.ts 文件,如果使用 ./src 文件夹,则放在其中。

由于 Server Components 不能写入 cookies,您需要中间件来刷新过期的 Auth 令牌并存储它们。

中间件负责:

  1. 刷新 Auth 令牌(通过调用 supabase.auth.getUser)。
  2. 将刷新的 Auth 令牌传递给 Server Components,这样它们就不会尝试自己刷新相同的令牌。这是通过 request.cookies.set 实现的。
  3. 将刷新的 Auth 令牌传递给浏览器,以便替换旧的令牌。这是通过 response.cookies.set 实现的。

复制适用于您应用的中间件代码。

添加一个 matcher,这样中间件就不会在不访问 Supabase 的路由上运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { type NextRequest } from 'next/server'import { updateSession } from '@/utils/supabase/middleware'export async function middleware(request: NextRequest) { return await updateSession(request)}export const config = { matcher: [ /* * 匹配所有请求路径,除了以下开头的路径: * - _next/static (静态文件) * - _next/image (图片优化文件) * - favicon.ico (favicon 文件) * 可以自由修改此模式以包含更多路径。 */ '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', ],}
5

创建登录页面

为您的应用创建一个登录页面。使用 Server Action 调用 Supabase 的 signup 函数。

由于 Supabase 是从 Action 调用的,使用 @/utils/supabase/server.ts 中定义的客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { login, signup } from './actions'export default function LoginPage() { return ( <form> <label htmlFor="email">邮箱</label> <input id="email" name="email" type="email" required /> <label htmlFor="password">密码</label> <input id="password" name="password" type="password" required /> <button formAction={login}>登录</button> <button formAction={signup}>注册</button> </form> )}
6

更改认证确认路径

如果您启用了邮件确认(默认情况下),新用户注册后会收到一封确认邮件。

更改邮件模板以支持服务器端认证流程。

转到仪表板中的 Auth 模板 页面。在 Confirm signup 模板中,将 {{ .ConfirmationURL }} 更改为 {{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email

7

为认证确认创建路由处理器

auth/confirm 创建一个 Route Handler。当用户点击确认邮件链接时,将他们的安全代码交换为 Auth 令牌。

由于这是一个 Router Handler,使用 @/utils/supabase/server.ts 中的 Supabase 客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { type EmailOtpType } from '@supabase/supabase-js'import { type NextRequest } from 'next/server'import { createClient } from '@/utils/supabase/server'import { redirect } from 'next/navigation'export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) const token_hash = searchParams.get('token_hash') const type = searchParams.get('type') as EmailOtpType | null const next = searchParams.get('next') ?? '/' if (token_hash && type) { const supabase = await createClient() const { error } = await supabase.auth.verifyOtp({ type, token_hash, }) if (!error) { // 将用户重定向到指定的重定向 URL 或应用的根目录 redirect(next) } } // 将用户重定向到错误页面并显示一些说明 redirect('/error')}
8

从 Server Component 访问用户信息

Server Components 可以读取 cookies,因此您可以获取认证状态和用户信息。

由于您是从 Server Component 调用 Supabase,使用 @/utils/supabase/server.ts 中创建的客户端。

创建一个 private 页面,只有登录的用户才能访问。该页面显示他们的邮箱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { redirect } from 'next/navigation'import { createClient } from '@/utils/supabase/server'export default async function PrivatePage() { const supabase = await createClient() const { data, error } = await supabase.auth.getUser() if (error || !data?.user) { redirect('/login') } return <p>你好 {data.user.email}</p>}

恭喜完成

您已经成功完成了所有步骤!让我们回顾一下您已经实现的内容:

  • 从服务器操作(Server Action)调用Supabase
  • 从服务器组件(Server Component)调用Supabase
  • 设置Supabase客户端工具函数,以便从客户端组件调用Supabase(例如用于建立实时订阅)
  • 配置中间件自动刷新Supabase认证会话

现在您可以在客户端或服务器代码中使用所有Supabase功能了!