You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 是浏览器的安全机制,禁止客户端 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 03:53:16