Next.js 13如何将客户端PocketBase会话传递至服务端?
解决方案:在Next.js 13 App Router中传递PocketBase会话到服务端
要解决Server Components无法获取PocketBase会话的问题,核心是把客户端存储的会话令牌传递到服务端,让服务端的PocketBase实例能验证用户身份。以下是具体实现步骤:
1. 登录时同步会话到Cookies
客户端登录成功后,除了依赖PocketBase的authStore,还要把令牌写入Next.js的Cookies,这样服务端能读取到。修改你的登录组件:
'use client'; import { useRouter } from "next/navigation"; import { useState } from "react"; import PocketBase from 'pocketbase'; const pb = new PocketBase('https://pockettest.lightin.io'); export default function Page() { const [email, setEmail] = useState("breval.lefloch@gmail.com") const [password, setPassword] = useState("123456") const router = useRouter(); const handleSubmit = async (e: { preventDefault: () => void; }) => { e.preventDefault() if (email && password) { const authData = await pb.collection('users').authWithPassword( email.toString(), password.toString(), ); // 将令牌写入Cookies,设置HttpOnly和Secure增强安全性 document.cookie = `pb_auth=${pb.authStore.token}; path=/; HttpOnly; Secure; SameSite=Lax`; // 登录成功后跳转 router.push('/protected'); } } return ( <div className="flex items-center justify-center h-screen"> <form onSubmit={handleSubmit} method="post" className="bg-white p-6 rounded-lg shadow-md"> <h2 className="text-lg font-medium mb-4">Login</h2> <div className="mb-4"> <label htmlFor="email" className="block font-medium mb-2">Email :</label> <input onChange={e => setEmail(e.target.value)} type="text" name="email" value={email.toString()} required className="border border-gray-400 p-2 w-full" /> </div> <div className="mb-4"> <label htmlFor="password" className="block font-medium mb-2">Password :</label> <input onChange={e => setPassword(e.target.value)} type="password" name="password" value={password.toString()} required className="border border-gray-400 p-2 w-full" /> </div> <button className="bg-blue-500 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-full"> Submit </button> </form> </div> ) }
2. 创建服务端PocketBase工具函数
在项目中创建一个服务端可用的工具函数,用于从Cookies获取令牌并初始化PocketBase实例:
// app/lib/pocketbase-server.ts import PocketBase from 'pocketbase'; import { cookies } from 'next/headers'; export async function createPocketBaseInstance() { const pb = new PocketBase('https://pockettest.lightin.io'); // 从Cookies读取令牌 const authCookie = cookies().get('pb_auth'); if (authCookie) { pb.authStore.loadFromCookie(`pb_auth=${authCookie.value}`); // 验证令牌有效性,自动刷新过期令牌 try { if (pb.authStore.isValid) { await pb.collection('users').authRefresh(); } } catch (_) { // 令牌无效则清空 pb.authStore.clear(); } } return pb; }
3. 在Server Components中使用
现在可以在Server Components里调用这个工具函数,获取已验证的PocketBase实例,进行数据查询:
// app/protected/page.tsx import { redirect } from 'next/navigation'; import { createPocketBaseInstance } from '../lib/pocketbase-server'; export default async function ProtectedPage() { const pb = await createPocketBaseInstance(); // 检查用户是否已认证 if (!pb.authStore.isValid) { // 未认证则重定向到登录页 return redirect('/login'); } // 使用PocketBase查询数据 const user = pb.authStore.model; const records = await pb.collection('your-collection').getList(1, 20); return ( <div> <h1>欢迎, {user?.email}</h1> <ul> {records.items.map(item => ( <li key={item.id}>{item.title}</li> ))} </ul> </div> ); }
4. 处理登出逻辑
登出时需要同时清空客户端的authStore和服务端Cookies:
// app/components/LogoutButton.tsx 'use client'; import { useRouter } from 'next/navigation'; import PocketBase from 'pocketbase'; const pb = new PocketBase('https://pockettest.lightin.io'); export default function LogoutButton() { const router = useRouter(); const handleLogout = () => { pb.authStore.clear(); // 清空Cookies document.cookie = `pb_auth=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; HttpOnly; Secure; SameSite=Lax`; router.push('/login'); }; return <button onClick={handleLogout}>登出</button>; }
关键注意事项
- 安全性:设置Cookies的
HttpOnly和Secure属性,防止XSS攻击,仅在HTTPS环境下传输。 - 令牌刷新:服务端每次初始化实例时调用
authRefresh,确保令牌始终有效。 - Server Components特性:Server Components中无法直接访问浏览器的
window或document,必须通过Cookies传递会话信息。
内容的提问来源于stack exchange,提问作者Unknown
相关产品推荐
相关产品推荐

