字
字节笔记本
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),
})工作示例
分享: