为 Next.js 配置服务端认证
Next.js 提供了两种模式:App Router 和 Pages Router。您可以使用其中任何一种策略来设置服务器端认证。甚至可以在同一个应用中同时使用两者。
安装 Supabase 包
安装 @supabase/supabase-js
包和辅助包 @supabase/ssr
。
1npm install @supabase/supabase-js @supabase/ssr
设置环境变量
在项目根目录下创建 .env.local
文件。
填写您的 NEXT_PUBLIC_SUPABASE_URL
和 NEXT_PUBLIC_SUPABASE_ANON_KEY
:
Project URL
Anon key
12NEXT_PUBLIC_SUPABASE_URL=<your_supabase_project_url>NEXT_PUBLIC_SUPABASE_ANON_KEY=<your_supabase_anon_key>
编写创建 Supabase 客户端的工具函数
要从 Next.js 应用访问 Supabase,您需要两种类型的 Supabase 客户端:
- 客户端组件客户端 - 用于在浏览器中运行的 Client Components 中访问 Supabase。
- 服务端组件客户端 - 用于在仅服务器端运行的 Server Components、Server Actions 和 Route Handlers 中访问 Supabase。
创建一个 utils/supabase
文件夹,为每种客户端类型创建一个文件。然后复制每种客户端类型的工具函数。
12345678import { createBrowserClient } from '@supabase/ssr'export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! )}
连接中间件
在项目根目录下创建 middleware.ts
文件,如果使用 ./src
文件夹,则放在其中。
由于 Server Components 不能写入 cookies,您需要中间件来刷新过期的 Auth 令牌并存储它们。
中间件负责:
- 刷新 Auth 令牌(通过调用
supabase.auth.getUser
)。 - 将刷新的 Auth 令牌传递给 Server Components,这样它们就不会尝试自己刷新相同的令牌。这是通过
request.cookies.set
实现的。 - 将刷新的 Auth 令牌传递给浏览器,以便替换旧的令牌。这是通过
response.cookies.set
实现的。
复制适用于您应用的中间件代码。
添加一个 matcher,这样中间件就不会在不访问 Supabase 的路由上运行。
保护页面时要小心。服务器从 cookies 中获取用户会话,任何人都可以伪造 cookies。
始终使用 supabase.auth.getUser()
来保护页面和用户数据。
在服务器代码(如中间件)中,永远不要 信任 supabase.auth.getSession()
。它不能保证重新验证 Auth 令牌。
可以信任 getUser()
,因为它每次都会向 Supabase Auth 服务器发送请求以重新验证 Auth 令牌。
12345678910111213141516171819import { 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)$).*)', ],}
创建登录页面
为您的应用创建一个登录页面。使用 Server Action 调用 Supabase 的 signup 函数。
由于 Supabase 是从 Action 调用的,使用 @/utils/supabase/server.ts
中定义的客户端。
注意,在调用 Supabase 之前调用了 cookies
,这将 fetch 调用从 Next.js 的缓存中排除。这对于认证的数据获取很重要,以确保用户只能访问自己的数据。
查看 Next.js 文档了解更多关于 选择退出数据缓存 的信息。
1234567891011121314import { 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> )}
更改认证确认路径
如果您启用了邮件确认(默认情况下),新用户注册后会收到一封确认邮件。
更改邮件模板以支持服务器端认证流程。
转到仪表板中的 Auth 模板 页面。在 Confirm signup
模板中,将 {{ .ConfirmationURL }}
更改为 {{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email
。
为认证确认创建路由处理器
为 auth/confirm
创建一个 Route Handler。当用户点击确认邮件链接时,将他们的安全代码交换为 Auth 令牌。
由于这是一个 Router Handler,使用 @/utils/supabase/server.ts
中的 Supabase 客户端。
12345678910111213141516171819202122232425262728import { 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')}
从 Server Component 访问用户信息
Server Components 可以读取 cookies,因此您可以获取认证状态和用户信息。
由于您是从 Server Component 调用 Supabase,使用 @/utils/supabase/server.ts
中创建的客户端。
创建一个 private
页面,只有登录的用户才能访问。该页面显示他们的邮箱。
保护页面时要小心。服务器从 cookies 中获取用户会话,任何人都可以伪造 cookies。
始终使用 supabase.auth.getUser()
来保护页面和用户数据。
在 Server Components 中,永远不要 信任 supabase.auth.getSession()
。它不能保证重新验证 Auth 令牌。
可以信任 getUser()
,因为它每次都会向 Supabase Auth 服务器发送请求以重新验证 Auth 令牌。
1234567891011121314import { 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功能了!