14 KiB
14 KiB
NextAuth.js (Auth.js) 集成指南
📋 方案介绍
NextAuth.js 是最流行的 Next.js 认证解决方案,完全开源免费。
核心优势
- ✅ 5分钟集成 - 超级简单
- ✅ 完全免费 - 无用户数限制
- ✅ 多种登录方式 - 邮箱、Google、GitHub、微信等
- ✅ 安全可靠 - OAuth 2.0 标准
- ✅ 完美集成 - 专为 Next.js 设计
🚀 快速集成步骤
第一步:安装依赖
cd /Users/lanmu/Desktop/xiaso
npm install next-auth@latest
第二步:配置环境变量
创建 .env.local 文件:
# NextAuth 密钥(必须)
NEXTAUTH_SECRET=your-secret-key-here-replace-with-random-string
NEXTAUTH_URL=https://scs.flnxi.com
# Google OAuth(可选)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# GitHub OAuth(可选)
GITHUB_ID=your-github-client-id
GITHUB_SECRET=your-github-client-secret
生成密钥:
openssl rand -base64 32
第三步:创建 Auth API 路由
创建文件:app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import GitHubProvider from "next-auth/providers/github"
import CredentialsProvider from "next-auth/providers/credentials"
const handler = NextAuth({
providers: [
// 邮箱密码登录
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: "邮箱", type: "email", placeholder: "user@example.com" },
password: { label: "密码", type: "password" }
},
async authorize(credentials, req) {
// 这里添加您的用户验证逻辑
// 例如:从数据库查询用户
const user = { id: "1", name: "用户", email: credentials?.email }
if (user) {
return user
}
return null
}
}),
// Google 登录(需要配置 OAuth)
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
// GitHub 登录(需要配置 OAuth)
GitHubProvider({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
],
// 自定义登录页面
pages: {
signIn: '/auth/signin',
// signOut: '/auth/signout',
// error: '/auth/error',
},
// 回调函数
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id
}
return token
},
async session({ session, token }) {
if (session.user) {
session.user.id = token.id as string
}
return session
},
},
// 会话策略
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 天
},
})
export { handler as GET, handler as POST }
第四步:创建登录页面
创建文件:app/auth/signin/page.tsx
'use client'
import { signIn } from 'next-auth/react'
import { useState } from 'react'
import { Mail, Lock, Chrome, Github } from 'lucide-react'
export default function SignInPage() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
await signIn('credentials', {
email,
password,
callbackUrl: '/',
})
setLoading(false)
}
return (
<div className="min-h-screen bg-gradient-to-b from-apple-gray-50 to-white flex items-center justify-center px-4">
<div className="max-w-md w-full">
{/* Logo */}
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-apple-gray-900 mb-2">
登录 FLNXI
</h1>
<p className="text-apple-gray-600">
欢迎回来,请登录您的账户
</p>
</div>
{/* 登录卡片 */}
<div className="bg-white rounded-2xl apple-shadow-lg p-8">
{/* 邮箱密码登录 */}
<form onSubmit={handleSubmit} className="space-y-4 mb-6">
<div>
<label className="block text-sm font-medium text-apple-gray-700 mb-2">
邮箱地址
</label>
<div className="relative">
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-apple-gray-400" />
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full pl-10 pr-4 py-3 border border-apple-gray-200 rounded-xl focus:ring-2 focus:ring-apple-blue focus:border-transparent outline-none"
placeholder="your@email.com"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-apple-gray-700 mb-2">
密码
</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-apple-gray-400" />
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pl-10 pr-4 py-3 border border-apple-gray-200 rounded-xl focus:ring-2 focus:ring-apple-blue focus:border-transparent outline-none"
placeholder="••••••••"
required
/>
</div>
</div>
<button
type="submit"
disabled={loading}
className="w-full py-3 bg-gradient-to-r from-apple-blue to-blue-600 text-white font-semibold rounded-xl button-hover apple-shadow disabled:opacity-50"
>
{loading ? '登录中...' : '登录'}
</button>
</form>
{/* 分割线 */}
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-apple-gray-200" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-4 bg-white text-apple-gray-500">
或使用第三方登录
</span>
</div>
</div>
{/* 第三方登录 */}
<div className="space-y-3">
<button
onClick={() => signIn('google', { callbackUrl: '/' })}
className="w-full flex items-center justify-center space-x-3 py-3 border-2 border-apple-gray-200 rounded-xl hover:bg-apple-gray-50 transition-all font-medium"
>
<Chrome className="w-5 h-5 text-red-500" />
<span>使用 Google 登录</span>
</button>
<button
onClick={() => signIn('github', { callbackUrl: '/' })}
className="w-full flex items-center justify-center space-x-3 py-3 border-2 border-apple-gray-200 rounded-xl hover:bg-apple-gray-50 transition-all font-medium"
>
<Github className="w-5 h-5" />
<span>使用 GitHub 登录</span>
</button>
</div>
{/* 底部链接 */}
<div className="mt-6 text-center text-sm text-apple-gray-600">
还没有账户?
<a href="/auth/signup" className="text-apple-blue hover:underline ml-1">
立即注册
</a>
</div>
</div>
{/* 返回首页 */}
<div className="text-center mt-6">
<a href="/" className="text-apple-gray-600 hover:text-apple-blue transition-colors">
← 返回首页
</a>
</div>
</div>
</div>
)
}
第五步:添加 Session Provider
修改 app/layout.tsx:
import { getServerSession } from "next-auth/next"
import SessionProvider from '@/components/SessionProvider'
export default async function RootLayout({
children,
}: {
children: React.ReactNode
}) {
const session = await getServerSession()
return (
<html lang="zh-CN">
<body>
<SessionProvider session={session}>
{children}
</SessionProvider>
</body>
</html>
)
}
创建 components/SessionProvider.tsx:
'use client'
import { SessionProvider as NextAuthSessionProvider } from 'next-auth/react'
export default function SessionProvider({
children,
session,
}: {
children: React.ReactNode
session: any
}) {
return (
<NextAuthSessionProvider session={session}>
{children}
</NextAuthSessionProvider>
)
}
第六步:在 Header 中添加登录按钮
修改 components/Header.tsx,添加登录状态:
'use client'
import { useSession, signIn, signOut } from 'next-auth/react'
import { User, LogOut } from 'lucide-react'
export default function Header({ scrolled }: HeaderProps) {
const { data: session, status } = useSession()
return (
<header>
{/* ... 其他代码 ... */}
{/* 添加登录/用户信息 */}
<div className="flex items-center space-x-4">
{status === 'loading' ? (
<div className="w-8 h-8 rounded-full bg-apple-gray-200 animate-pulse" />
) : session ? (
// 已登录
<div className="flex items-center space-x-3">
<span className="text-sm text-apple-gray-700">
{session.user?.name || session.user?.email}
</span>
<button
onClick={() => signOut()}
className="flex items-center space-x-2 px-4 py-2 text-apple-gray-700 hover:text-apple-blue transition-colors"
>
<LogOut className="w-4 h-4" />
<span>退出</span>
</button>
</div>
) : (
// 未登录
<button
onClick={() => signIn()}
className="flex items-center space-x-2 px-5 py-2 bg-apple-blue text-white rounded-full text-sm font-medium button-hover apple-shadow"
>
<User className="w-4 h-4" />
<span>登录</span>
</button>
)}
</div>
</header>
)
}
🎨 高级功能
1. 添加注册页面
创建 app/auth/signup/page.tsx(类似登录页面)
2. 保护页面(需要登录才能访问)
// app/dashboard/page.tsx
import { getServerSession } from "next-auth/next"
import { redirect } from "next/navigation"
export default async function DashboardPage() {
const session = await getServerSession()
if (!session) {
redirect('/auth/signin')
}
return <div>受保护的页面内容</div>
}
3. 添加微信登录
npm install next-auth-wechat
// 在 [...nextauth]/route.ts 中添加
import WeChatProvider from "next-auth-wechat"
providers: [
WeChatProvider({
clientId: process.env.WECHAT_APP_ID!,
clientSecret: process.env.WECHAT_APP_SECRET!,
}),
]
📦 部署配置
1. 更新环境变量
在服务器上设置环境变量:
ssh root@45.249.208.118
# 编辑环境变量
cd /opt/flnxi-website
nano .env.local
添加:
NEXTAUTH_SECRET=<生成的密钥>
NEXTAUTH_URL=https://scs.flnxi.com
2. 更新 Docker 配置
修改 docker-compose.yml:
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "4782:3000"
environment:
- NODE_ENV=production
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- NEXTAUTH_URL=${NEXTAUTH_URL}
env_file:
- .env.local
restart: unless-stopped
3. 重新构建部署
docker-compose down
docker-compose up -d --build
🔐 配置第三方登录
Google OAuth 配置
- 访问 Google Cloud Console
- 创建新项目
- 启用 Google+ API
- 创建 OAuth 2.0 凭据
- 添加授权重定向 URI:
https://scs.flnxi.com/api/auth/callback/google
GitHub OAuth 配置
- 访问 GitHub Settings > Developer settings
- 创建 New OAuth App
- 添加授权回调 URL:
https://scs.flnxi.com/api/auth/callback/github
📊 功能对比
| 功能 | NextAuth.js | Clerk | Logto |
|---|---|---|---|
| 开源免费 | ✅ | ⚠️ 有限制 | ✅ |
| 集成难度 | 简单 | 超简单 | 中等 |
| 用户数限制 | 无限制 | 10k MAU | 无限制 |
| UI 自定义 | 完全自定义 | 有限 | 可自定义 |
| 中文支持 | 需自己实现 | 支持 | 原生支持 |
| 需要额外服务 | ❌ | ❌ | ✅ |
✅ 集成检查清单
- 安装 next-auth 依赖
- 创建 API 路由
[...nextauth]/route.ts - 配置环境变量(NEXTAUTH_SECRET)
- 创建登录页面
- 添加 SessionProvider
- 更新 Header 组件
- 测试邮箱登录
- (可选)配置 Google OAuth
- (可选)配置 GitHub OAuth
- 部署到服务器
- 测试生产环境
🎯 推荐时间线
- 5分钟:基础集成(邮箱登录)
- +10分钟:美化登录页面
- +15分钟:添加 Google/GitHub 登录
- +10分钟:测试和部署
总计:40分钟完成完整集成
📞 获取帮助
如果遇到问题:
- 查看 NextAuth.js 官方文档
- 搜索 GitHub Issues
- 加入 Discord 社区
准备好了吗?让我开始帮您集成! 🚀