SvelteKit表单操作如何设置page.data及读取HttpOnly令牌Cookie
一、用表单操作(Form Actions)将用户信息存入 page.data
表单操作是 SvelteKit 推荐的服务器端表单处理方案,能天然规避客户端操作的安全隐患,同时统一管理用户状态的注入逻辑。
1. 创建服务器端表单处理逻辑(+page.server.ts)
在登录页面目录下新建该文件,负责登录请求的后端交互、Cookie 设置、用户信息解码,并通过页面加载逻辑控制登录状态跳转:
import { fail, redirect } from '@sveltejs/kit'; import type { Actions, PageServerLoad } from './$types'; import { signin } from '$lib/services/usersService'; import { USER_COOKIE_TOKEN_NAME, routes } from '$lib/configs'; import { jwtDecode } from 'jwt-decode'; import type { User, UserSignin } from '$lib/models/users'; // 页面加载时自动检查登录状态,已登录则跳转首页 export const load: PageServerLoad = async ({ cookies }) => { const token = cookies.get(USER_COOKIE_TOKEN_NAME); let user: User | null = null; if (token) { try { user = jwtDecode<User>(token); } catch (e) { // 令牌无效时自动清除Cookie cookies.delete(USER_COOKIE_TOKEN_NAME); } if (user) throw redirect(303, routes.HOME); } return { user }; }; // 处理登录表单提交 export const actions: Actions = { default: async ({ request, cookies }) => { const formData = await request.formData(); const email = formData.get('mail')?.toString(); const password = formData.get('password')?.toString(); // 表单字段校验 if (!email || !password) { return fail(400, { error: '邮箱和密码不能为空', mail: email }); } const userSignin: UserSignin = { username: email, password }; try { const tokenResponse = await signin(userSignin); if (!tokenResponse?.access_token) { return fail(401, { error: '登录失败,用户名或密码错误', mail: email }); } // 设置 HttpOnly 安全Cookie(生产环境必须开启Secure) cookies.set(USER_COOKIE_TOKEN_NAME, tokenResponse.access_token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', path: '/', maxAge: 60 * 60 * 24 * 7 // 有效期7天 }); // 登录成功直接跳转首页 throw redirect(303, routes.HOME); } catch (error) { return fail(500, { error: '服务器错误,请稍后重试', mail: email }); } } };
2. 简化登录页面组件(+page.svelte)
移除客户端自定义提交逻辑,直接利用 SvelteKit 表单操作的自动绑定:
<script lang="ts"> import Button from "$lib/components/Button.svelte"; import Input from "$lib/components/Input.svelte"; export let data; export let form; // 接收表单操作返回的错误信息 </script> <h1>Войти в аккаунт</h1> <form method="POST"> <Input name="mail" type="email" placeholder="Почта" value={form?.mail ?? ''} /> <Input name="password" type="password" placeholder="Пароль" /> {#if form?.error} <p style="color: red; margin: 1rem 0;">{form.error}</p> {/if} <Button type="submit" text="Войти" /> </form>
3. 全局注入用户信息到所有子组件
在项目根目录的 +layout.server.ts 中添加加载逻辑,让所有页面的 page.data 自动携带用户信息:
import type { LayoutServerLoad } from './$types'; import { USER_COOKIE_TOKEN_NAME } from '$lib/configs'; import { jwtDecode } from 'jwt-decode'; import type { User } from '$lib/models/users'; export const load: LayoutServerLoad = async ({ cookies }) => { const token = cookies.get(USER_COOKIE_TOKEN_NAME); let user: User | null = null; if (token) { try { user = jwtDecode<User>(token); } catch (e) { cookies.delete(USER_COOKIE_TOKEN_NAME); } } return { user }; };
之后所有子组件只需通过 export let data; 或 $page.data.user 即可访问当前用户信息。
二、HttpOnly Cookie 的读取问题
HttpOnly 是浏览器的安全机制,禁止客户端 JavaScript 直接读取这类 Cookie,目的是防范 XSS 攻击窃取令牌。正确的处理方式是:
- 浏览器会自动在所有同域请求中携带 HttpOnly Cookie,无需手动处理
- 所有需要验证用户身份的逻辑,放在 SvelteKit 的服务器端代码(
+page.server.ts、+server.ts等)中,通过cookies.get(USER_COOKIE_TOKEN_NAME)读取令牌 - 客户端如需判断登录状态,直接使用根布局注入的
$page.data.user,无需触碰 Cookie
示例客户端组件判断登录状态:
<script lang="ts"> import { page } from "$app/stores"; </script> {#if $page.data.user} <span>欢迎,{$page.data.user.username}</span> {:else} <a href="/login">登录</a> {/if}
关键注意事项
- 服务器端调用
signin时,axios 无需处理跨域问题(服务器端请求不存在跨域限制),确保后端 API 地址配置正确 - 生产环境必须开启 Cookie 的
secure: true,仅允许在 HTTPS 环境下传输 - 令牌解码仅用于获取用户信息,如需验证令牌有效性,建议调用后端专门的验证接口,避免客户端或服务器端解码带来的安全风险
内容的提问来源于stack exchange,提问作者fgrd4035
相关产品推荐
相关产品推荐

