Remix Session管理 #
一、Session概述 #
Session 用于在请求之间存储用户数据,常用于用户认证、购物车等场景。
二、创建Session存储 #
2.1 Cookie Session #
tsx
import { createCookieSessionStorage } from "@remix-run/node";
const sessionSecret = process.env.SESSION_SECRET;
if (!sessionSecret) {
throw new Error("SESSION_SECRET must be set");
}
export const sessionStorage = createCookieSessionStorage({
cookie: {
name: "__session",
httpOnly: true,
maxAge: 60 * 60 * 24 * 7, // 7天
path: "/",
sameSite: "lax",
secrets: [sessionSecret],
secure: process.env.NODE_ENV === "production",
},
});
export const { getSession, commitSession, destroySession } = sessionStorage;
2.2 文件系统Session #
tsx
import { createFileSessionStorage } from "@remix-run/node";
export const sessionStorage = createFileSessionStorage({
dir: "./sessions",
cookie: {
name: "__session",
secrets: [process.env.SESSION_SECRET],
},
});
2.3 数据库Session #
tsx
import { createSessionStorage } from "@remix-run/node";
export const sessionStorage = createSessionStorage({
cookie: {
name: "__session",
secrets: [process.env.SESSION_SECRET],
},
async createData(data, expires) {
const id = uuid();
await db.session.create({
data: { id, data, expiresAt: expires },
});
return id;
},
async readData(id) {
const session = await db.session.findUnique({ where: { id } });
return session?.data;
},
async updateData(id, data, expires) {
await db.session.update({
where: { id },
data: { data, expiresAt: expires },
});
},
async deleteData(id) {
await db.session.delete({ where: { id } });
},
});
三、使用Session #
3.1 获取Session #
tsx
import type { LoaderFunctionArgs } from "@remix-run/node";
import { getSession } from "~/session.server";
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
const userId = session.get("userId");
return json({ userId });
}
3.2 设置Session数据 #
tsx
import type { ActionFunctionArgs } from "@remix-run/node";
import { getSession, commitSession } from "~/session.server";
export async function action({ request }: ActionFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
session.set("userId", user.id);
session.set("userName", user.name);
return redirect("/dashboard", {
headers: {
"Set-Cookie": await commitSession(session),
},
});
}
3.3 销毁Session #
tsx
export async function action({ request }: ActionFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
return redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session),
},
});
}
四、Flash消息 #
4.1 设置Flash消息 #
tsx
export async function action({ request }: ActionFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
session.flash("success", "登录成功!");
return redirect("/dashboard", {
headers: {
"Set-Cookie": await commitSession(session),
},
});
}
4.2 读取Flash消息 #
tsx
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
const success = session.get("success");
return json(
{ success },
{
headers: {
"Set-Cookie": await commitSession(session),
},
}
);
}
五、用户认证 #
5.1 登录 #
tsx
import { json, redirect } from "@remix-run/node";
import bcrypt from "bcryptjs";
import { getSession, commitSession } from "~/session.server";
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const email = formData.get("email");
const password = formData.get("password");
const user = await getUserByEmail(email);
if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
return json({ error: "邮箱或密码错误" }, { status: 400 });
}
const session = await getSession(request.headers.get("Cookie"));
session.set("userId", user.id);
return redirect("/dashboard", {
headers: {
"Set-Cookie": await commitSession(session),
},
});
}
5.2 登出 #
tsx
export async function action({ request }: ActionFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
return redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session),
},
});
}
5.3 获取当前用户 #
tsx
export async function requireUserId(request: Request) {
const session = await getSession(request.headers.get("Cookie"));
const userId = session.get("userId");
if (!userId) {
throw redirect("/login");
}
return userId;
}
export async function requireUser(request: Request) {
const userId = await requireUserId(request);
const user = await getUser(userId);
if (!user) {
throw redirect("/login");
}
return user;
}
六、Session工具函数 #
6.1 封装Session操作 #
tsx
export async function createUserSession(
userId: string,
redirectTo: string
) {
const session = await getSession();
session.set("userId", userId);
return redirect(redirectTo, {
headers: {
"Set-Cookie": await commitSession(session),
},
});
}
export async function getUser(request: Request) {
const session = await getSession(request.headers.get("Cookie"));
const userId = session.get("userId");
if (!userId) return null;
return await db.user.findUnique({ where: { id: userId } });
}
七、安全最佳实践 #
7.1 Cookie安全配置 #
tsx
export const sessionStorage = createCookieSessionStorage({
cookie: {
name: "__session",
httpOnly: true, // 防止XSS
secure: true, // 仅HTTPS
sameSite: "lax", // 防止CSRF
secrets: [sessionSecret],
maxAge: 60 * 60 * 24 * 7,
path: "/",
},
});
7.2 Session过期 #
tsx
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
const expiresAt = session.get("expiresAt");
if (expiresAt && new Date(expiresAt) < new Date()) {
throw redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session),
},
});
}
// ...
}
八、总结 #
本章我们学习了:
- Session存储:Cookie、文件、数据库存储
- Session操作:获取、设置、销毁
- Flash消息:一次性消息
- 用户认证:登录、登出、获取用户
- 安全实践:Cookie安全配置
核心要点:
- 选择合适的Session存储方式
- 正确设置Cookie安全属性
- 使用Flash消息传递提示
最后更新:2026-03-28