用户认证集成
用户认证是大多数 SaaS 应用的核心功能。Vibe CLI 支持多种主流的认证服务提供商,让你可以快速为应用添加用户注册、登录、权限管理等功能。
🚀 快速开始
bash
# 交互式安装认证集成
vibe install auth
# 直接指定提供商
vibe install auth --provider=clerk
📦 支持的提供商
Clerk - 现代化认证服务
特点:
- 🎨 开箱即用的 UI 组件
- 🔐 支持多种社交登录
- 📱 多因素认证 (MFA)
- 👥 用户管理面板
- 🌍 国际化支持
安装:
bash
vibe install auth --provider=clerk
配置示例:
bash
? 是否启用社交登录: Yes
? 选择社交登录平台: Google, GitHub, Apple
? 是否启用多因素认证: Yes
? 自定义域名 (可选): auth.myapp.com
NextAuth - Next.js 官方方案
特点:
- ⚡ Next.js 深度集成
- 🔧 高度可定制
- 🛡️ 安全性优先
- 📚 丰富的提供商支持
- 🗄️ 多种数据库支持
安装:
bash
vibe install auth --provider=nextauth
配置示例:
bash
? 选择认证提供商: Google, GitHub, Discord
? 选择数据库: PostgreSQL (Supabase)
? 是否启用邮箱验证: Yes
? JWT 密钥策略: Auto-generate
Supabase Auth - 开源解决方案
特点:
- 🆓 开源免费
- 🗄️ 与数据库深度集成
- 📧 内置邮箱验证
- 🔑 行级安全 (RLS)
- 📱 移动端支持
安装:
bash
vibe install auth --provider=supabase
配置示例:
bash
? Supabase 项目 URL: https://xxx.supabase.co
? 是否启用社交登录: Yes
? 选择社交登录平台: Google, GitHub
? 是否启用邮箱确认: Yes
🛠️ 安装后配置
1. 环境变量设置
根据选择的提供商,需要配置相应的环境变量:
Clerk
bash
# .env.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
CLERK_SECRET_KEY=sk_test_xxx
NextAuth
bash
# .env.local
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx
GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx
Supabase
bash
# .env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJxxx
SUPABASE_SERVICE_ROLE_KEY=eyJxxx
2. 获取 API 密钥
Clerk
- 访问 Clerk Dashboard
- 创建新应用或选择现有应用
- 在 "API Keys" 页面复制密钥
- 配置认证设置和社交登录
NextAuth
- Google: 访问 Google Cloud Console
- GitHub: 访问 GitHub Developer Settings
- Discord: 访问 Discord Developer Portal
Supabase
- 访问 Supabase Dashboard
- 创建新项目或选择现有项目
- 在 "Settings > API" 页面复制密钥
- 配置认证设置
📝 使用示例
Clerk 集成示例
tsx
pages/_app.tsx
import { ClerkProvider } from '@clerk/nextjs'
function MyApp({ Component, pageProps }) {
return (
<ClerkProvider>
<Component {...pageProps} />
</ClerkProvider>
)
}
export default MyApp
tsx
components/AuthButton.tsx
import { SignInButton, SignOutButton, useUser } from '@clerk/nextjs'
export function AuthButton() {
const { isSignedIn, user } = useUser()
if (isSignedIn) {
return (
<div>
<span>Hello, {user.firstName}!</span>
<SignOutButton />
</div>
)
}
return <SignInButton />
}
NextAuth 集成示例
tsx
pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import GitHubProvider from 'next-auth/providers/github'
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
],
})
tsx
components/AuthButton.tsx
import { useSession, signIn, signOut } from 'next-auth/react'
export function AuthButton() {
const { data: session } = useSession()
if (session) {
return (
<div>
<span>Hello, {session.user?.name}!</span>
<button onClick={() => signOut()}>退出登录</button>
</div>
)
}
return <button onClick={() => signIn()}>登录</button>
}
Supabase 集成示例
tsx
lib/supabase.ts
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
tsx
components/AuthButton.tsx
import { useState, useEffect } from 'react'
import { supabase } from '../lib/supabase'
export function AuthButton() {
const [user, setUser] = useState(null)
useEffect(() => {
const { data: { user } } = supabase.auth.getUser()
setUser(user)
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
setUser(session?.user ?? null)
}
)
return () => subscription.unsubscribe()
}, [])
const handleSignIn = async () => {
await supabase.auth.signInWithOAuth({
provider: 'google',
})
}
const handleSignOut = async () => {
await supabase.auth.signOut()
}
if (user) {
return (
<div>
<span>Hello, {user.email}!</span>
<button onClick={handleSignOut}>退出登录</button>
</div>
)
}
return <button onClick={handleSignIn}>登录</button>
}
🔧 高级配置
自定义认证流程
tsx
components/CustomAuth.tsx
import { useState } from 'react'
import { useAuth } from '../hooks/useAuth'
export function CustomAuth() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const { signIn, signUp, loading } = useAuth()
const handleSignIn = async (e) => {
e.preventDefault()
await signIn(email, password)
}
const handleSignUp = async (e) => {
e.preventDefault()
await signUp(email, password)
}
return (
<form>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="邮箱"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="密码"
/>
<button onClick={handleSignIn} disabled={loading}>
登录
</button>
<button onClick={handleSignUp} disabled={loading}>
注册
</button>
</form>
)
}
权限管理
tsx
components/ProtectedRoute.tsx
import { useAuth } from '../hooks/useAuth'
import { useRouter } from 'next/router'
import { useEffect } from 'react'
interface ProtectedRouteProps {
children: React.ReactNode
requiredRole?: string
}
export function ProtectedRoute({ children, requiredRole }: ProtectedRouteProps) {
const { user, loading } = useAuth()
const router = useRouter()
useEffect(() => {
if (!loading && !user) {
router.push('/login')
}
if (requiredRole && user?.role !== requiredRole) {
router.push('/unauthorized')
}
}, [user, loading, requiredRole, router])
if (loading) {
return <div>加载中...</div>
}
if (!user) {
return null
}
if (requiredRole && user.role !== requiredRole) {
return <div>权限不足</div>
}
return <>{children}</>
}
🎨 自定义样式
Clerk 样式自定义
tsx
pages/_app.tsx
import { ClerkProvider } from '@clerk/nextjs'
const clerkAppearance = {
variables: {
colorPrimary: '#3b82f6',
colorText: '#1f2937',
colorBackground: '#ffffff',
colorInputBackground: '#f9fafb',
colorInputText: '#1f2937',
borderRadius: '8px',
},
elements: {
formButtonPrimary: 'bg-blue-600 hover:bg-blue-700',
card: 'shadow-lg',
},
}
function MyApp({ Component, pageProps }) {
return (
<ClerkProvider appearance={clerkAppearance}>
<Component {...pageProps} />
</ClerkProvider>
)
}
NextAuth 样式自定义
tsx
pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth'
export default NextAuth({
theme: {
colorScheme: 'light',
brandColor: '#3b82f6',
logo: '/logo.png',
},
pages: {
signIn: '/auth/signin',
signUp: '/auth/signup',
error: '/auth/error',
},
})
🐛 常见问题
1. 认证状态不同步
问题: 用户登录后状态没有更新
解决方案:
tsx
// 确保在组件顶层使用认证钩子
const { user, loading } = useAuth()
// 或者手动刷新状态
const refreshAuth = async () => {
await supabase.auth.getSession()
}
2. 重定向问题
问题: 登录后没有正确重定向
解决方案:
tsx
// 设置正确的回调 URL
const handleSignIn = async () => {
await signIn('google', {
callbackUrl: '/dashboard'
})
}
3. 环境变量问题
问题: API 密钥配置错误
解决方案:
bash
# 检查环境变量
echo $NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
# 重启开发服务器
npm run dev
📚 最佳实践
- 安全性: 始终使用 HTTPS,保护敏感信息
- 用户体验: 提供清晰的错误信息和加载状态
- 性能: 使用适当的缓存策略
- 可访问性: 确保认证界面符合无障碍标准
- 测试: 编写认证流程的单元测试和集成测试
🎯 下一步
完成认证集成后,你可以: