概述
本文檔介紹了如何使用 Next-Auth 配置一個同時支持普通用戶和管理員用戶登錄的認證系統。
基本配置
首先,我們需要設置 Next-Auth 的基本配置,包括提供者、回調函數和頁面路由。
import type { NextAuthConfig } from 'next-auth'
import type { User } from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import { LoginSchema } from '@/types/auth'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcryptjs'export const authOptions = {providers: [// 配置提供者],callbacks: {// 配置回調函數},pages: {// 配置頁面路由},
} satisfies NextAuthConfig
配置認證提供者
需要配置兩個 Credentials 提供者,分別用于普通用戶和管理員用戶的登錄。
providers: [Credentials({id: 'user-login', // 普通用戶登錄name: 'User Credentials',async authorize(credentials) {try {const validatedFields = LoginSchema.safeParse(credentials)if (!validatedFields.success) return nullconst { email, password } = validatedFields.dataconst user = await prisma.user.findUnique({where: { email },select: {id: true,email: true,password: true,username: true,avatar_url: true,},})if (!user?.password) return nullconst passwordsMatch = await bcrypt.compare(password, user.password)if (!passwordsMatch) return nullreturn {id: user.id,email: user.email,username: user.username || user.email?.split('@')[0] || '',avatar_url: user.avatar_url || '',role: 'user', // 添加角色標識}} catch (error) {console.error('User authorization error:', error)return null}},}),Credentials({id: 'admin-login', // 管理員登錄name: 'Admin Credentials',async authorize(credentials) {try {const validatedFields = LoginSchema.safeParse(credentials)if (!validatedFields.success) return nullconst { email, password } = validatedFields.dataconst admin = await prisma.adminUser.findUnique({where: { email },select: {id: true,email: true,password: true,username: true,avatar_url: true,role: true,},})if (!admin?.password) return nullconst passwordsMatch = await bcrypt.compare(password, admin.password)if (!passwordsMatch) return nullreturn {id: admin.id,email: admin.email,username: admin.username,avatar_url: admin.avatar_url || '',role: 'admin', // 添加角色標識}} catch (error) {console.error('Admin authorization error:', error)return null}},}),
],
配置回調函數
回調函數用于處理會話和 JWT 令牌的自定義邏輯。
callbacks: {async session({ session, token }) {if (session.user) {session.user.username = token.username as stringsession.user.id = token.sub as stringsession.user.avatar_url = token.avatar_url as stringsession.user.role = token.role as string // 添加角色}return session},async jwt({ token, user }) {if (user) {token.username = user.usernametoken.sub = user.idtoken.avatar_url = user.avatar_urltoken.role = user.role // 添加角色}return token},
},
配置頁面路由
配置自定義的登錄頁面和錯誤頁面。
pages: {signIn: '/login', // 普通用戶登錄頁error: '/auth/error', // 錯誤頁面
},
擴展 Session 類型
為了在 TypeScript 中正確識別自定義的會話屬性,我們需要擴展 Next-Auth 的類型定義。
// types/next-auth.d.ts
import { DefaultSession } from "next-auth"declare module "next-auth" {interface Session {user: {id: stringusername: stringavatar_url: stringrole: string} & DefaultSession["user"]}interface User {id: stringusername: stringavatar_url: stringrole: string}
}
區分用戶和管理員
如果需要在會話中明確區分用戶和管理員,可以修改 session 回調函數:
async session({ session, token }) {if (session.user) {session.user.username = token.username as stringsession.user.id = token.sub as stringsession.user.avatar_url = token.avatar_url as stringsession.user.role = token.role as string}// 添加區分的用戶和管理員對象if (token.role === 'admin') {session.admin = {id: token.sub as string,email: session.user?.email,username: token.username as string,avatar_url: token.avatar_url as string,role: 'admin'}} else {session.admin = null // 如果不是管理員,設置為 null}return session
},
同時更新類型定義:
// types/next-auth.d.ts
interface Session {user: {id: stringusername: stringavatar_url: stringrole: string} & DefaultSession["user"]admin: {id: stringemail?: string | nullusername: stringavatar_url: stringrole: string} | null
}
使用方式
在組件中使用會話信息:
import { useSession } from "next-auth/react"function MyComponent() {const { data: session } = useSession()// 檢查是否是管理員if (session?.user?.role === 'admin') {// 管理員邏輯} else {// 普通用戶邏輯}// 或者使用擴展的 admin 對象if (session?.admin) {// 管理員邏輯} else {// 普通用戶邏輯}
}
登錄方式
在登錄表單中,可以通過指定 providerId 來選擇使用哪個認證提供者:
import { signIn } from "next-auth/react"// 普通用戶登錄
signIn("user-login", { email, password,redirect: true,callbackUrl: "/dashboard"
})// 管理員登錄
signIn("admin-login", { email, password,redirect: true,callbackUrl: "/admin/dashboard"
})