Next.js中的身份验证模式Skill auth-patterns

这个技能用于在Next.js应用程序中实现身份验证和授权功能,涵盖多种策略和库,包括NextAuth.js、中间件保护、会话管理和JWT等。关键词:Next.js, 身份验证, NextAuth, Auth.js, 中间件, 会话管理。

前端开发 0 次安装 0 次浏览 更新于 3/8/2026

name: 身份验证模式 description: 当用户询问“Next.js中的身份验证”、“NextAuth”、“Auth.js”、“中间件身份验证”、“保护路由”、“会话管理”、“JWT”、“登录流程”,或需要指导在Next.js应用程序中实现身份验证和授权时,应使用此技能。 version: 1.0.0

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

  // 重定向已验证用户离开身份验证页面
  if (authRoutes.some(route => pathname.startsWith(route))) {
    if (token) {
      return NextResponse.redirect(new URL('/dashboard', request.url))
    }
    return NextResponse.next()
  }

  // 保护路由
  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)
    // 令牌有效,继续
    return NextResponse.next()
  } catch {
    // 令牌无效
    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}</>
}

// 使用
export default async function AdminPage() {
  return (
    <AdminOnly>
      <AdminDashboard />
    </AdminOnly>
  )
}

会话存储选项

JWT(无状态)

// auth.ts
export const { auth } = NextAuth({
  session: { strategy: 'jwt' },
  // JWT 存储在 cookie 中,无需数据库
})

数据库会话

// auth.ts
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'

export const { auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  session: { strategy: '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>
  )
}

安全最佳实践

  1. 在生产环境中使用 HTTPS
  2. 设置安全的 cookie 标志(HttpOnly、Secure、SameSite)
  3. 实施 CSRF 保护(NextAuth 内置)
  4. 验证重定向 URL 以防止开放重定向
  5. 使用环境变量 存储密钥
  6. 在身份验证端点上实施速率限制
  7. 使用 bcrypt 或 argon2 哈希密码

资源

详细模式,请参见:

  • references/middleware-auth.md - 高级中间件模式
  • references/session-management.md - 会话策略
  • examples/nextauth-setup.md - 完整的 NextAuth.js 设置