关于 auth-patterns
auth-patterns 是面向 Next.js 应用开发者的身份验证技能,涵盖 NextAuth.js (Auth.js)、中间件保护、会话和基于角色的访问控制,也介绍会话存储选项与安全实践。
主要功能
- 01配置 NextAuth.js v5
- 02通过中间件保护路由
- 03在服务端和客户端获取会话
- 04基于角色的访问控制
- 05JWT 与数据库会话
- 06身份验证安全实践
适用场景
- 01为 Next.js 应用添加登录
- 02保护仪表板和 API 路由
- 03按用户角色限制页面访问
- 04选择会话存储策略
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davepoon/ buildwithclaude --skill auth-patterns安装后在 Claude Code 中输入/auth-patterns确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/nextauth-setup.mdreferences/middleware-auth.mdsession-management.md详细介绍
Next.js 中的身份验证模式
概述
Next.js 支持多种身份验证策略。本技能涵盖常见模式,包括 NextAuth.js (Auth.js)、基于中间件的保护和会话管理。
身份验证库
| 库 | 适用场景 |
|---|---|
| NextAuth.js (Auth.js) | 功能全面且支持身份提供方的身份验证 |
| Clerk | 托管式身份验证服务 |
| Lucia | 轻量、灵活的身份验证 |
| Supabase Auth | Supabase 生态系统 |
| Custom JWT | 完全掌控 |
NextAuth.js v5 设置
安装
npm install next-auth@beta
配置
// auth.ts
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Credentials from 'next-auth/providers/credentials'
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
GitHub({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
authorize: async (credentials) => {
const user = await getUserByEmail(credentials.email)
if (!user || !verifyPassword(credentials.password, user.password)) {
return null
}
return user
},
}),
],
callbacks: {
authorized: async ({ auth }) => {
return !!auth
},
},
})
API 路由处理程序
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'
export const { GET, POST } = handlers
中间件保护
// middleware.ts
export { auth as middleware } from '@/auth'
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
}
获取会话数据
在服务端组件中
// app/dashboard/page.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
export default async function DashboardPage() {
const session = await auth()
if (!session) {
redirect('/login')
}
return (
<div>
<h1>Welcome, {session.user?.name}</h1>
</div>
)
}
在客户端组件中
// components/user-menu.tsx
'use client'
import { useSession } from 'next-auth/react'
export function UserMenu() {
const { data: session, status } = useSession()
if (status === 'loading') {
return <div>Loading...</div>
}
if (!session) {
return <SignInButton />
}
return (
<div>
<span>{session.user?.name}</span>
<SignOutButton />
</div>
)
}
设置会话提供方
// app/providers.tsx
'use client'
import { SessionProvider } from 'next-auth/react'
export function Providers({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>
}
// app/layout.tsx
import { Providers } from './providers'
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}
登录/登出组件
// components/auth-buttons.tsx
import { signIn, signOut } from '@/auth'
export function SignInButton() {
return (
<form
action={async () => {
'use server'
await signIn('github')
}}
>
<button type="submit">Sign in with GitHub</button>
</form>
)
}
export function SignOutButton() {
return (
<form
action={async () => {
'use server'
await signOut()
}}
>
<button type="submit">Sign out</button>
</form>
)
}
基于中间件的身份验证
基本模式
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const protectedRoutes = ['/dashboard', '/settings', '/api/protected']
const authRoutes = ['/login', '/signup']
export function middleware(request: NextRequest) {
const token = request.cookies.get('session')?.value
const { pathname } = request.nextUrl
// Redirect authenticated users away from auth pages
if (authRoutes.some(route => pathname.startsWith(route))) {
if (token) {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
return NextResponse.next()
}
// Protect routes
if (protectedRoutes.some(route => pathname.startsWith(route))) {
if (!token) {
const loginUrl = new URL('/login', request.url)
loginUrl.searchParams.set('callbackUrl', pathname)
return NextResponse.redirect(loginUrl)
}
}
return NextResponse.next()
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
使用 JWT 验证
// middleware.ts
import { NextResponse } from 'next/server'
import { jwtVerify } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET)
export async function middleware(request: NextRequest) {
const token = request.cookies.get('token')?.value
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
try {
const { payload } = await jwtVerify(token, secret)
// Token is valid, continue
return NextResponse.next()
} catch {
// Token is invalid
return NextResponse.redirect(new URL('/login', request.url))
}
}
基于角色的访问控制
扩展会话类型
// types/next-auth.d.ts
import { DefaultSession } from 'next-auth'
declare module 'next-auth' {
interface Session {
user: {
role: 'user' | 'admin'
} & DefaultSession['user']
}
}
// auth.ts
export const { handlers, auth } = NextAuth({
callbacks: {
session: ({ session, token }) => ({
...session,
user: {
...session.user,
role: token.role,
},
}),
jwt: ({ token, user }) => {
if (user) {
token.role = user.role
}
return token
},
},
})
基于角色的组件
// components/admin-only.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
export async function AdminOnly({ children }: { children: React.ReactNode }) {
const session = await auth()
if (session?.user?.role !== 'admin') {
redirect('/unauthorized')
}
return <>{children}</>
}
// Usage
export default async function AdminPage() {
return (
<AdminOnly>
<AdminDashboard />
</AdminOnly>
)
}
会话存储选项
JWT(无状态)
// auth.ts
export const { auth } = NextAuth({
session: { strategy: 'jwt' },
// JWT stored in cookies, no database needed
})
数据库会话
// auth.ts
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'
export const { auth } = NextAuth({
adapter: PrismaAdapter(prisma),
session: { strategy: 'database' },
// Sessions stored in database
})
自定义登录页面
// app/login/page.tsx
'use client'
import { signIn } from 'next-auth/react'
import { useSearchParams } from 'next/navigation'
export default function LoginPage() {
const searchParams = useSearchParams()
const callbackUrl = searchParams.get('callbackUrl') || '/dashboard'
return (
<div className="flex flex-col gap-4">
<button
onClick={() => signIn('github', { callbackUrl })}
className="btn"
>
Sign in with GitHub
</button>
<button
onClick={() => signIn('google', { callbackUrl })}
className="btn"
>
Sign in with Google
</button>
</div>
)
}
安全最佳实践
- 在生产环境中使用 HTTPS
- 设置安全的 Cookie 标志(HttpOnly、Secure、SameSite)
- 实施 CSRF 保护(NextAuth 内置)
- 验证重定向 URL,以防止开放重定向
- 使用环境变量存储密钥
- 对身份验证端点实施速率限制
- 使用 bcrypt 或 argon2 对密码进行哈希处理
资源
如需了解详细模式,请参阅:
references/middleware-auth.md- 高级中间件模式references/session-management.md- 会话策略examples/nextauth-setup.md- 完整的 NextAuth.js 设置
评论
支持 Markdown