会话管理
会话的创建、存储和校验机制。
Raypx 使用 Better Auth 的 tanstackStartCookies() 插件管理用户会话。会话数据存储在数据库中,会话标识通过 Cookie 在客户端和服务端之间传递。
数据库表
会话相关数据存储在以下数据库表中:
session 表
存储活跃的用户会话:
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid | 主键,使用 UUID v7 |
token | text | 会话 Token,唯一索引 |
expires_at | timestamptz | 会话过期时间 |
user_id | text | 关联用户 ID,级联删除 |
ip_address | text | 客户端 IP 地址 |
user_agent | text | 客户端 User-Agent |
last_active | timestamptz | 最后活跃时间 |
created_at | timestamptz | 创建时间 |
updated_at | timestamptz | 更新时间,自动更新 |
verification 表
存储邮箱验证和密码重置的 Token:
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid | 主键,使用 UUID v7 |
identifier | text | 标识符(通常是邮箱地址) |
value | text | Token 值 |
expires_at | timestamptz | Token 过期时间 |
created_at | timestamptz | 创建时间 |
updated_at | timestamptz | 更新时间 |
TanStack Start Cookies 插件
Raypx 使用 tanstackStartCookies() 插件将会话管理与 TanStack Start 的请求上下文集成:
// packages/auth/src/index.ts
import { tanstackStartCookies } from "better-auth/tanstack-start"
return betterAuth({
// ...
plugins: [tanstackStartCookies()],
})该插件负责:
- 从请求中读取 Cookie 获取会话 Token
- 在响应中设置包含会话 Token 的 Cookie
- 与 TanStack Start 的 SSR 请求/响应生命周期无缝协作
服务端会话校验
在服务端获取当前用户的会话:
import { auth } from "@raypx/auth"
// 在 API 路由或 Server Function 中
const session = await auth.api.getSession({
headers: request.headers,
})
if (!session) {
// 用户未登录
}
const user = session.user路由守卫
使用 TanStack Start 中间件在路由层面保护需要认证的页面。
创建认证中间件
// src/middleware/auth.ts
import { auth } from "@raypx/auth"
import { createMiddleware } from "@tanstack/react-start"
export const authMiddleware = createMiddleware().server(async ({ next, request }) => {
const session = await auth.api.getSession({
headers: request.headers,
})
return next({
context: { session },
})
})在 Server Function 中使用
// src/functions/get-user.ts
import { createServerFn } from "@tanstack/react-start"
import { authMiddleware } from "@/middleware/auth"
export const getUser = createServerFn({ method: "GET" })
.middleware([authMiddleware])
.handler(async (ctx) => {
return ctx.context.session
})在路由的 beforeLoad 中使用
import { createFileRoute } from "@tanstack/react-router"
import { getUser } from "@/functions/get-user"
export const Route = createFileRoute("/dashboard")({
beforeLoad: async () => {
const session = await getUser()
if (!session) {
throw redirect({ to: "/sign-in" })
}
},
})会话过期
会话有过期机制,由 session.expires_at 字段控制:
- 每次请求时,Better Auth 会检查会话是否过期
- 过期的会话会被自动拒绝
last_active字段记录用户最后活跃时间
密码重置时会撤销该用户的所有会话(revokeSessionsOnPasswordReset: true),确保账号安全。