ByteNoteByteNote

字节笔记本

2026年5月3日

TanStack Start - 认证实现 (Authentication Implementation)

API中转
¥120

使用 TanStack Start 构建自己的认证系统。DIY 实现提供完全控制、无供应商锁定、自定义需求和成本控制。

核心构建块

1. 认证服务端函数

tsx
import { createServerFn } from '@tanstack/react-start'
import { redirect } from '@tanstack/react-router'

export const loginFn = createServerFn({ method: 'POST' })
  .inputValidator((data: { email: string; password: string }) => data)
  .handler(async ({ data }) => {
    const user = await authenticateUser(data.email, data.password)
    if (!user) return { error: 'Invalid credentials' }

    const session = await useAppSession()
    await session.update({ userId: user.id, email: user.email })
    throw redirect({ to: '/dashboard' })
  })

export const logoutFn = createServerFn({ method: 'POST' }).handler(async () => {
  const session = await useAppSession()
  await session.clear()
  throw redirect({ to: '/' })
})

2. 会话管理

tsx
import { useSession } from '@tanstack/react-start/server'

type SessionData = {
  userId?: string
  email?: string
  role?: string
}

export function useAppSession() {
  return useSession<SessionData>({
    name: 'app-session',
    password: process.env.SESSION_SECRET!,
    cookie: {
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'lax',
      httpOnly: true,
    },
  })
}

3. 路由保护

tsx
// routes/_authed.tsx
export const Route = createFileRoute('/_authed')({
  beforeLoad: async ({ location }) => {
    const user = await getCurrentUserFn()
    if (!user) {
      throw redirect({ to: '/login', search: { redirect: location.href } })
    }
    return { user }
  },
})

4. 认证上下文

tsx
export function AuthProvider({ children }: { children: ReactNode }) {
  const { data: user, isLoading, refetch } = useServerFn(getCurrentUserFn)
  return (
    <AuthContext.Provider value={{ user, isLoading, refetch }}>
      {children}
    </AuthContext.Provider>
  )
}

实现模式

邮箱/密码认证

tsx
export const registerFn = createServerFn({ method: 'POST' })
  .inputValidator((data: { email: string; password: string; name: string }) => data)
  .handler(async ({ data }) => {
    const existingUser = await getUserByEmail(data.email)
    if (existingUser) return { error: 'User already exists' }

    const hashedPassword = await bcrypt.hash(data.password, 12)
    const user = await createUser({ ...data, password: hashedPassword })

    const session = await useAppSession()
    await session.update({ userId: user.id })
    return { success: true }
  })

基于角色的访问控制 (RBAC)

tsx
export const roles = { USER: 'user', ADMIN: 'admin', MODERATOR: 'moderator' } as const

export function hasPermission(userRole: Role, requiredRole: Role): boolean {
  const hierarchy = { user: 0, moderator: 1, admin: 2 }
  return hierarchy[userRole] >= hierarchy[requiredRole]
}

安全最佳实践

密码安全

tsx
import bcrypt from 'bcryptjs'
const hashedPassword = await bcrypt.hash(password, 12)

速率限制

tsx
const loginAttempts = new Map<string, { count: number; resetTime: number }>()

export const rateLimitLogin = (ip: string): boolean => {
  const now = Date.now()
  const attempts = loginAttempts.get(ip)
  if (!attempts || now > attempts.resetTime) {
    loginAttempts.set(ip, { count: 1, resetTime: now + 15 * 60 * 1000 })
    return true
  }
  if (attempts.count >= 5) return false
  attempts.count++
  return true
}

输入验证

tsx
import { z } from 'zod'

const loginSchema = z.object({
  email: z.string().email().max(255),
  password: z.string().min(8).max(100),
})

工作示例

分享: