Next.js 认证怎么做?NextAuth.js 配置 OAuth 和凭证登录实战
Next.js 认证最主流的方案是 NextAuth.js(v5 改名 Auth.js)。它处理了 OAuth、JWT、Session 管理等所有细节,30 分钟就能搭好 Google/GitHub 登录。
最快上手:NextAuth.js
bashnpm install next-auth@beta
typescript// app/api/auth/[...nextauth]/route.ts import NextAuth from 'next-auth'; import GitHub from 'next-auth/providers/github'; export const { handlers, auth, signIn, signOut } = NextAuth({ providers: [ GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, }), ], pages: { signIn: '/login', // 自定义登录页 }, }); export const { GET, POST } = handlers;
typescript// middleware.ts — 保护路由 export { auth as middleware } from './app/api/auth/[...nextauth]/route'; export const config = { matcher: ['/dashboard/:path*'], };
三步:配置 provider → 创建 API 路由 → 加中间件保护。用户访问 /dashboard 时如果没有登录,自动跳转到 /login。
在 Server Component 里获取 Session
tsximport { auth } from './app/api/auth/[...nextauth]/route'; export default async function Dashboard() { const session = await auth(); if (!session) { redirect('/login'); } return <h1>欢迎, {session.user.name}</h1>; }
auth() 在服务端获取 session,不需要客户端 JavaScript。这是 RSC 的优势——认证逻辑完全在服务端,客户端零开销。
在 Client Component 里获取 Session
tsx'use client' import { useSession } from 'next-auth/react'; export default function Profile() { const { data: session, status } = useSession(); if (status === 'loading') return <p>加载中...</p>; if (!session) return <p>请先登录</p>; return <p>{session.user.name}</p>; }
useSession 通过轮询 /api/auth/session 获取 session。更高效的方式是用 SessionProvider:
tsx// app/providers.tsx 'use client' import { SessionProvider } from 'next-auth/react'; export function Providers({ children }) { return <SessionProvider>{children}</SessionProvider>; }
包在 layout 里后,useSession 不再轮询,而是通过 Context 共享 session 数据。
自定义登录页
默认登录页太简陋。自定义页面:
tsx// app/login/page.tsx import { signIn } from '@/auth'; export default function LoginPage() { return ( <form action={async () => { 'use server' await signIn('github'); }}> <button type="submit">用 GitHub 登录</button> </form> ); }
用 Server Action 触发 signIn,比客户端 signIn() 更简洁。
凭证登录(用户名密码)
OAuth 不够时,加 Credentials provider:
typescriptimport Credentials from 'next-auth/providers/credentials'; import bcrypt from 'bcryptjs'; export const { handlers, auth } = NextAuth({ providers: [ Credentials({ credentials: { email: { label: 'Email', type: 'email' }, password: { label: 'Password', type: 'password' }, }, async authorize(credentials) { const user = await db.user.findUnique({ where: { email: credentials.email }, }); if (!user) return null; const valid = await bcrypt.compare(credentials.password, user.passwordHash); if (!valid) return null; return { id: user.id, name: user.name, email: user.email }; }, }), ], });
authorize 返回 null 表示认证失败,返回 user 对象表示成功。密码必须用 bcrypt 哈希,不要存明文。
常见问题
Session 过期后页面不刷新:用 SessionProvider 的 refetchInterval 定时刷新:<SessionProvider refetchInterval={300}> 每 5 分钟检查一次。
OAuth 回调 404:确保回调 URL 在 OAuth provider 里配置正确。GitHub 在 Settings > Developer > OAuth Apps 里配。
部署后 Cookie 不工作:NEXTAUTH_URL 环境变量必须设为生产域名。Vercel 部署时自动设置,其他平台需要手动配。