Next.js 身份验证 🔐
🎯 概述
身份验证是保护应用安全的关键。Next.js 支持多种认证方案,包括 NextAuth.js、JWT、Session 等。
📦 NextAuth.js (推荐)
安装
bash
npm install next-auth基本配置
tsx
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth'
import GithubProvider from 'next-auth/providers/github'
import CredentialsProvider from 'next-auth/providers/credentials'
const handler = NextAuth({
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: "邮箱", type: "email" },
password: { label: "密码", type: "password" }
},
async authorize(credentials) {
const user = await verifyUser(credentials)
if (user) {
return user
}
return null
}
})
],
pages: {
signIn: '/login',
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id
}
return token
},
async session({ session, token }) {
session.user.id = token.id
return session
}
}
})
export { handler as GET, handler as POST }使用认证
tsx
// app/profile/page.tsx
import { getServerSession } from 'next-auth'
import { redirect } from 'next/navigation'
export default async function Profile() {
const session = await getServerSession()
if (!session) {
redirect('/login')
}
return <div>欢迎,{session.user.name}</div>
}
// components/LoginButton.tsx
'use client'
import { signIn, signOut, useSession } from 'next-auth/react'
export default function LoginButton() {
const { data: session } = useSession()
if (session) {
return (
<button onClick={() => signOut()}>
退出
</button>
)
}
return (
<button onClick={() => signIn()}>
登录
</button>
)
}🚀 JWT 认证
创建 JWT 工具
tsx
// lib/jwt.ts
import jwt from 'jsonwebtoken'
const SECRET = process.env.JWT_SECRET!
export function signToken(payload: any) {
return jwt.sign(payload, SECRET, { expiresIn: '7d' })
}
export function verifyToken(token: string) {
try {
return jwt.verify(token, SECRET)
} catch {
return null
}
}登录 API
tsx
// app/api/login/route.ts
import { NextResponse } from 'next/server'
import { signToken } from '@/lib/jwt'
export async function POST(request: Request) {
const { email, password } = await request.json()
const user = await verifyCredentials(email, password)
if (!user) {
return NextResponse.json(
{ error: '凭据无效' },
{ status: 401 }
)
}
const token = signToken({ userId: user.id })
return NextResponse.json({ token, user })
}中间件保护
tsx
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { verifyToken } from '@/lib/jwt'
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')?.value
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
const payload = verifyToken(token)
if (!payload) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*']
}📝 Session 认证
使用 Cookies
tsx
// app/api/login/route.ts
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
const { email, password } = await request.json()
const user = await verifyCredentials(email, password)
if (!user) {
return NextResponse.json({ error: '登录失败' }, { status: 401 })
}
const sessionId = await createSession(user.id)
cookies().set('session', sessionId, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7 // 7 days
})
return NextResponse.json({ user })
}获取当前用户
tsx
// lib/auth.ts
import { cookies } from 'next/headers'
export async function getCurrentUser() {
const sessionId = cookies().get('session')?.value
if (!sessionId) {
return null
}
const session = await getSession(sessionId)
if (!session) {
return null
}
return session.user
}📚 最佳实践
1. 保护 API 路由
tsx
// lib/auth.ts
export async function requireAuth(request: Request) {
const token = request.headers.get('Authorization')?.split(' ')[1]
if (!token) {
throw new Error('未授权')
}
const payload = verifyToken(token)
if (!payload) {
throw new Error('令牌无效')
}
return payload
}
// app/api/posts/route.ts
import { requireAuth } from '@/lib/auth'
export async function POST(request: Request) {
try {
const user = await requireAuth(request)
// 创建文章
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 401 })
}
}2. 密码加密
bash
npm install bcrypttsx
import bcrypt from 'bcrypt'
// 注册时加密密码
const hashedPassword = await bcrypt.hash(password, 10)
// 登录时验证密码
const isValid = await bcrypt.compare(password, user.hashedPassword)3. 刷新令牌
tsx
// app/api/refresh/route.ts
export async function POST(request: Request) {
const { refreshToken } = await request.json()
const payload = verifyToken(refreshToken)
if (!payload) {
return NextResponse.json({ error: '令牌无效' }, { status: 401 })
}
const newToken = signToken({ userId: payload.userId })
return NextResponse.json({ token: newToken })
}🔗 相关资源
下一步:学习 Next.js 数据库集成。