Raypx

会话管理

会话的创建、存储和校验机制。

Raypx 使用 Better Auth 的 tanstackStartCookies() 插件管理用户会话。会话数据存储在数据库中,会话标识通过 Cookie 在客户端和服务端之间传递。

数据库表

会话相关数据存储在以下数据库表中:

session 表

存储活跃的用户会话:

字段类型说明
iduuid主键,使用 UUID v7
tokentext会话 Token,唯一索引
expires_attimestamptz会话过期时间
user_idtext关联用户 ID,级联删除
ip_addresstext客户端 IP 地址
user_agenttext客户端 User-Agent
last_activetimestamptz最后活跃时间
created_attimestamptz创建时间
updated_attimestamptz更新时间,自动更新

verification 表

存储邮箱验证和密码重置的 Token:

字段类型说明
iduuid主键,使用 UUID v7
identifiertext标识符(通常是邮箱地址)
valuetextToken 值
expires_attimestamptzToken 过期时间
created_attimestamptz创建时间
updated_attimestamptz更新时间

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),确保账号安全。

On this page