Next.js从入门到实战保姆级教程:认证鉴权与中间件

Next.js 系统性教学:中间件与国际化功能深入剖析 在构建现代 Web 应用时,开发者往往需要处理许多复杂的功能,如 中间件 和 国际化。这些功能不仅能增强应用的灵活性,还能改善用户体验。Next.js 提供了强大的中间件功能和国际化支持,帮助开发者轻松应对这些挑战。本文将通过详细的实例,讲解如何在 Next.js 中实现中间件和国际化,从而提升应用的功能性和可用性。 阅读详情

本系列文章将围绕Next.js技术栈,旨在为AI Agent开发者提供一套完整的客户端侧工程实践指南。

认证(Authentication) 解决"你是谁"的问题,鉴权(Authorization) 解决"你能做什么"的问题。这两者构成了应用安全的基石,实施不当将导致严重的安全漏洞。

一、认证方案选型

在 Next.js 生态中实现认证,主要有三种技术路线:

快速开发
标准需求

高度定制
企业内建

托管服务
全功能

需要认证功能

项目复杂度与需求

Auth.js
原NextAuth.js

手动实现 JWT

Clerk / Supabase Auth

✅ 支持OAuth社交登录
✅ 内置Session管理
✅ App Router深度集成

✅ 完全自定义控制
⚠️ 需编写更多代码
⚠️ 自行处理安全细节

✅ 功能最全面
✅ 零维护成本
⚠️ 产生服务费用
⚠️ 厂商锁定风险

方案选择建议:

场景推荐方案理由
初创项目/MVPAuth.js快速搭建,社区活跃
企业内部系统手动JWT已有认证基础设施
SaaS产品Clerk/Supabase节省开发时间,专注业务
高安全要求手动JWT + 审计完全掌控安全策略

对于大多数应用场景,Auth.js(原 NextAuth.js v5) 是最佳选择:支持数十种 OAuth 提供商(Google、GitHub、微信等),内置 Session 管理机制,与 Next.js App Router 深度集成。

本章重点讲解 Auth.js 方案,同时剖析 JWT 手动实现的底层原理。


二、Auth.js 完整认证实现

1. 安装与基础配置

npm install next-auth@beta

(1)实例化Next-Auth

// auth.ts(项目根目录)
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
import { db } from '@/lib/db';
import bcrypt from 'bcryptjs';
import type { DefaultSession } from 'next-auth';

// 扩展 Session 类型
declare module 'next-auth' {
  interface Session {
    user: {
      id: string;
      role: string;
    } & DefaultSession['user'];
  }
}

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    // GitHub OAuth 登录
    GitHub({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),

    // Google OAuth 登录
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),

    // 邮箱密码登录(Credentials Provider)
    Credentials({
      name: '邮箱密码',
      credentials: {
        email: { label: '邮箱', type: 'email', placeholder: 'example@email.com' },
        password: { label: '密码', type: 'password' },
      },
      async authorize(credentials) {
        // 参数验证
        if (!credentials?.email || !credentials?.password) {
          return null;
        }

        // 查询用户
        const user = await db.users.findUnique({
          where: { email: credentials.email as string },
        });

        // 用户不存在或无密码
        if (!user || !user.hashedPassword) {
          return null;
        }

        // 验证密码
        const isValid = await bcrypt.compare(
          credentials.password as string,
          user.hashedPassword
        );

        if (!isValid) {
          return null;
        }

        // 返回用户信息(敏感信息不返回)
        return {
          id: user.id,
          name: user.name,
          email: user.email,
          image: user.avatar,
          role: user.role,
        };
      },
    }),
  ],

  // Session 配置
  session: {
    strategy: 'jwt',  // 使用 JWT,无需数据库存储 Session
    maxAge: 30 * 24 * 60 * 60,  // 30 天过期
  },

  // 自定义页面路径
  pages: {
    signIn: '/login',      // 自定义登录页
    signOut: '/logout',    // 自定义登出页
    error: '/auth/error',  // 错误页面
  },

  // Callbacks:自定义认证流程
  callbacks: {
    /**
     * JWT Token 回调
     * 在 token 创建/更新时触发
     */
    async jwt({ token, user }) {
      // 首次登录时,将用户信息添加到 token
      if (user) {
        token.id = user.id;
        token.role = user.role;
      }
      return token;
    },

    /**
     * Session 回调
     * 每次读取 session 时触发
     */
    async session({ session, token }) {
      if (token && session.user) {
        session.user.id = token.id as string;
        session.user.role = token.role as string;
      }
      return session;
    },
  },

  // 调试模式(仅开发环境)
  debug: process.env.NODE_ENV === 'development',
});

(2)定义route handler

// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';

// 导出 GET 和 POST 处理器
export const { GET, POST } = handlers;

(3)填写配置信息

# .env.local
AUTH_SECRET="your-secret-key-min-32-chars"  # 使用 openssl rand -base64 32 生成
GITHUB_ID="your-github-app-id"
GITHUB_SECRET="your-github-app-secret"
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

安全提示:AUTH_SECRET 必须至少 32 个字符,生产环境务必使用强随机字符串。

2. 登录页面实现

// app/login/page.tsx
import { signIn } from '@/auth';
import { AuthError } from 'next-auth';
import { redirect } from 'next/navigation';
import Link from 'next/link';

interface LoginPageProps {
  searchParams: Promise<{ callbackUrl?: string; error?: string }>;
}

export default async function LoginPage({ 
  searchParams 
}: LoginPageProps) {
  const { callbackUrl, error } = await searchParams;

  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
      <div className="w-full max-w-md p-8 bg-white rounded-2xl shadow-lg border border-gray-100">
        <h1 className="text-2xl font-bold text-center mb-8 text-gray-900">
          欢迎登录
        </h1>

        {/* 错误提示 */}
        {error && (
          <div 
            className="mb-4 p-3 bg-red-50 text-red-700 rounded-lg text-sm border border-red-200"
            role="alert"
          >
            {error === 'CredentialsSignin'
              ? '邮箱或密码错误,请重试'
              : '登录失败,请稍后重试'}
          </div>
        )}

        {/* 邮箱密码登录表单 */}
        <form
          action={async (formData) => {
            'use server';
            try {
              await signIn('credentials', {
                email: formData.get('email'),
                password: formData.get('password'),
                redirectTo: callbackUrl || '/dashboard',
              });
            } catch (error) {
              if (error instanceof AuthError) {
                redirect(`/login?error=${error.type}`);
              }
              throw error;
            }
          }}
          className="space-y-4"
        >
          <div>
            <label 
              htmlFor="email" 
              className="block text-sm font-medium text-gray-700 mb-1"
            >
              邮箱地址
            </label>
            <input
              id="email"
              name="email"
              type="email"
              required
              autoComplete="email"
              className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              placeholder="your@email.com"
            />
          </div>

          <div>
            <label 
              htmlFor="password" 
              className="block text-sm font-medium text-gray-700 mb-1"
            >
              密码
            </label>
            <input
              id="password"
              name="password"
              type="password"
              required
              autoComplete="current-password"
              className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              placeholder="••••••••"
            />
          </div>

          <button
            type="submit"
            className="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
          >
            登录
          </button>
        </form>

        {/* 分隔线 */}
        <div className="my-6 flex items-center gap-4">
          <hr className="flex-1 border-gray-300" />
          <span className="text-gray-400 text-sm">或使用以下方式登录</span>
          <hr className="flex-1 border-gray-300" />
        </div>

        {/* OAuth 登录按钮 */}
        <div className="space-y-3">
          {/* GitHub 登录 */}
          <form
            action={async () => {
              'use server';
              await signIn('github', {
                redirectTo: callbackUrl || '/dashboard',
              });
            }}
          >
            <button
              type="submit"
              className="w-full flex items-center justify-center gap-2 border border-gray-300 py-2 rounded-lg hover:bg-gray-50 transition-colors"
            >
              <svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
                <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
              </svg>
              使用 GitHub 登录
            </button>
          </form>

          {/* Google 登录 */}
          <form
            action={async () => {
              'use server';
              await signIn('google', {
                redirectTo: callbackUrl || '/dashboard',
              });
            }}
          >
            <button
              type="submit"
              className="w-full flex items-center justify-center gap-2 border border-gray-300 py-2 rounded-lg hover:bg-gray-50 transition-colors"
            >
              <svg className="w-5 h-5" viewBox="0 0 24 24">
                <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
                <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
                <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
                <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
              </svg>
              使用 Google 登录
            </button>
          </form>
        </div>

        {/* 注册链接 */}
        <p className="mt-6 text-center text-sm text-gray-600">
          还没有账号?{' '}
          <Link 
            href="/register" 
            className="text-blue-600 hover:text-blue-700 font-medium"
          >
            立即注册
          </Link>
        </p>
      </div>
    </div>
  );
}

三、Session 获取与管理

Auth.js 提供了多种场景下的 Session 获取方式:

1. 服务端组件中获取

// app/dashboard/page.tsx
import { auth } from '@/auth';
import { redirect } from 'next/navigation';

export default async function DashboardPage() {
  // 获取当前会话
  const session = await auth();

  // 未登录则重定向
  if (!session?.user) {
    redirect('/login?callbackUrl=/dashboard');
  }

  return (
    <div className="container mx-auto py-8">
      <h1 className="text-2xl font-bold mb-4">
        欢迎,{session.user.name}
      </h1>
      <p className="text-gray-600">邮箱:{session.user.email}</p>
      <p className="text-gray-600">角色:{session.user.role}</p>
    </div>
  );
}

2. 客户端组件中获取

// components/UserMenu.tsx
'use client';

import { useSession, signOut } from 'next-auth/react';
import Image from 'next/image';
import Link from 'next/link';

export function UserMenu() {
  const { data: session, status } = useSession();

  // 加载中状态
  if (status === 'loading') {
    return (
      <div className="w-8 h-8 rounded-full bg-gray-200 animate-pulse" />
    );
  }

  // 未登录
  if (!session) {
    return (
      <Link 
        href="/login"
        className="px-4 py-2 text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
      >
        登录
      </Link>
    );
  }

  // 已登录
  return (
    <div className="flex items-center gap-3">
      {session.user.image && (
        <Image
          src={session.user.image}
          alt={`${session.user.name} 的头像`}
          width={32}
          height={32}
          className="rounded-full border border-gray-200"
        />
      )}
      <span className="text-sm text-gray-700">{session.user.name}</span>
      <button
        onClick={() => signOut({ callbackUrl: '/' })}
        className="text-sm text-gray-500 hover:text-red-600 transition-colors"
      >
        退出
      </button>
    </div>
  );
}

需在根布局中包裹 SessionProvider:

// app/layout.tsx
import { SessionProvider } from 'next-auth/react';
import { auth } from '@/auth';
import type { ReactNode } from 'react';

interface RootLayoutProps {
  children: ReactNode;
}

export default async function RootLayout({ 
  children 
}: RootLayoutProps) {
  const session = await auth();

  return (
    <html lang="zh-CN">
      <body>
        <SessionProvider session={session}>
          {children}
        </SessionProvider>
      </body>
    </html>
  );
}

四、Middleware:路由级认证保护

中间件(Middleware)运行在 Edge Runtime,在请求到达路由处理程序之前执行。这是实现路由级别认证保护的最佳位置——比在每个页面单独检查 Session 高效得多。

1. 基础路由保护

// middleware.ts(项目根目录)
import { auth } from '@/auth';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export default auth((req: NextRequest) => {
  const { nextUrl } = req;
  const session = req.auth;
  const isLoggedIn = !!session?.user;

  // 定义受保护路由
  const protectedRoutes = [
    '/dashboard',
    '/profile',
    '/settings',
    '/admin',
  ];

  const isProtectedRoute = protectedRoutes.some(route => 
    nextUrl.pathname.startsWith(route)
  );

  // 未登录访问受保护页面 → 重定向到登录页
  if (isProtectedRoute && !isLoggedIn) {
    const redirectUrl = new URL('/login', nextUrl);
    redirectUrl.searchParams.set('callbackUrl', nextUrl.pathname);
    return NextResponse.redirect(redirectUrl);
  }

  // 已登录访问登录/注册页 → 重定向到仪表板
  if (isLoggedIn && ['/login', '/register'].includes(nextUrl.pathname)) {
    return NextResponse.redirect(new URL('/dashboard', nextUrl));
  }

  return NextResponse.next();
});

// 配置中间件匹配规则(排除静态资源)
export const config = {
  matcher: [
    /*
     * 匹配所有路径,除了:
     * - api(API 路由)
     * - _next/static(静态文件)
     * - _next/image(图片优化)
     * - favicon.ico(网站图标)
     * - public 文件夹
     */
    '/((?!api|_next/static|_next/image|favicon.ico|public).*)',
  ],
};

2. 基于角色的访问控制(RBAC)

// middleware.ts
import { auth } from '@/auth';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export default auth((req: NextRequest) => {
  const { nextUrl } = req;
  const session = req.auth;
  const userRole = session?.user?.role;

  // 管理员路由保护
  if (nextUrl.pathname.startsWith('/admin')) {
    // 未登录
    if (!session?.user) {
      return NextResponse.redirect(new URL('/login', nextUrl));
    }
    
    // 非管理员
    if (userRole !== 'admin') {
      return NextResponse.redirect(new URL('/403', nextUrl));
    }
  }

  // 编辑者路由保护
  if (nextUrl.pathname.startsWith('/editor')) {
    if (!session?.user) {
      return NextResponse.redirect(new URL('/login', nextUrl));
    }
    
    const allowedRoles = ['admin', 'editor'];
    if (!allowedRoles.includes(userRole || '')) {
      return NextResponse.redirect(new URL('/403', nextUrl));
    }
  }

  return NextResponse.next();
});

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

五、手动实现 JWT 认证(深入理解)

虽然推荐使用 Auth.js,但理解 JWT 认证的底层原理对应对复杂场景至关重要。许多企业内部系统已有后端 API 提供 JWT,前端只需处理 Token 的存储和传递。

1. JWT 工作流程

数据库 服务端 客户端 用户 数据库 服务端 客户端 用户 提交邮箱密码 POST /api/login 验证用户凭证 返回用户信息 生成 JWT Token 返回 JWT 存储于 HttpOnly Cookie 访问受保护页面 携带 JWT Cookie 验证 JWT 有效性 返回数据

2. 安全 Token 管理

一般可以把Token存在HTTPOnly Cookie里。

// lib/session.ts
import { SignJWT, jwtVerify } from 'jose';
import { cookies } from 'next/headers';

// JWT 密钥(生产环境从环境变量读取)
const SECRET_KEY = new TextEncoder().encode(
  process.env.JWT_SECRET || 'fallback-secret-change-in-production'
);

/**
 * 创建 Session Token
 * @param userId - 用户ID
 * @param role - 用户角色
 */
export async function createSession(
  userId: string, 
  role: string
): Promise<void> {
  const token = await new SignJWT({ userId, role })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('7d')  // 7 天过期
    .sign(SECRET_KEY);

  const cookieStore = await cookies();

  // 将 JWT 存储在 HttpOnly Cookie 中
  cookieStore.set('session', token, {
    httpOnly: true,           // JavaScript 无法读取,防止 XSS
    secure: process.env.NODE_ENV === 'production',  // 仅 HTTPS
    sameSite: 'lax',          // 防止 CSRF
    maxAge: 7 * 24 * 60 * 60, // 7 天
    path: '/',
  });
}

/**
 * 验证并读取 Session
 * @returns Session 载荷或 null
 */
export async function getSession(): Promise<{ 
  userId: string; 
  role: string 
} | null> {
  const cookieStore = await cookies();
  const token = cookieStore.get('session')?.value;

  if (!token) {
    return null;
  }

  try {
    const { payload } = await jwtVerify(token, SECRET_KEY);
    return payload as { userId: string; role: string };
  } catch (error) {
    // Token 无效或已过期
    console.error('Invalid token:', error);
    return null;
  }
}

/**
 * 删除 Session(退出登录)
 */
export async function deleteSession(): Promise<void> {
  const cookieStore = await cookies();
  cookieStore.delete('session');
}

为什么使用 Cookie 而非 localStorage?
localStorage 可被 JavaScript 访问,易受 XSS 攻击(恶意脚本窃取 Token)。HttpOnly Cookie 无法被 JavaScript 访问,XSS 攻击无法窃取 Token。这是 Web 安全的核心实践之一。


六、安全防护最佳实践

1. 敏感信息绝不暴露于客户端

// ❌ 危险:在客户端存储权限信息
localStorage.setItem('isAdmin', 'true');

// ✅ 正确:仅在服务端验证权限
export async function DELETE(request: Request) {
  const session = await getSession();
  const user = await db.users.findUnique({ 
    where: { id: session?.userId } 
  });

  if (user?.role !== 'admin') {
    return new Response('Forbidden', { status: 403 });
  }

  await deleteData();
}

2. 密码哈希存储

import bcrypt from 'bcryptjs';

// 注册时:哈希密码
const saltRounds = 12;  // 10-12 为合理范围
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);
await db.users.create({ 
  data: { email, hashedPassword } 
});

// 登录时:验证密码
const isValid = await bcrypt.compare(plainPassword, hashedPassword);
if (!isValid) {
  throw new Error('密码错误');
}

绝对禁止明文存储密码,即使数据库泄露,哈希密码也能保护用户安全。

3. 限制登录尝试次数

// lib/rate-limit.ts
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL!,
  token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});

const MAX_ATTEMPTS = 5;
const LOCKOUT_DURATION = 15 * 60; // 15 分钟

/**
 * 检查登录速率限制
 */
export async function checkRateLimit(email: string): Promise<{
  allowed: boolean;
  remainingAttempts?: number;
  lockoutUntil?: Date;
}> {
  const key = `login_attempts:${email}`;
  const attempts = await redis.get<number>(key) || 0;

  if (attempts >= MAX_ATTEMPTS) {
    const ttl = await redis.ttl(key);
    return {
      allowed: false,
      lockoutUntil: new Date(Date.now() + ttl * 1000),
    };
  }

  return {
    allowed: true,
    remainingAttempts: MAX_ATTEMPTS - attempts,
  };
}

/**
 * 记录登录失败
 */
export async function recordFailedAttempt(email: string): Promise<void> {
  const key = `login_attempts:${email}`;
  await redis.incr(key);
  await redis.expire(key, LOCKOUT_DURATION);
}

/**
 * 重置登录尝试计数
 */
export async function resetLoginAttempts(email: string): Promise<void> {
  const key = `login_attempts:${email}`;
  await redis.del(key);
}

4. CSRF 防护

Auth.js 内置 CSRF 保护。若手动实现 JWT,需添加 CSRF Token:

// 生成 CSRF Token
import { randomBytes } from 'crypto';

export function generateCsrfToken(): string {
  return randomBytes(32).toString('hex');
}

// 验证 CSRF Token
export function verifyCsrfToken(
  tokenFromCookie: string,
  tokenFromBody: string
): boolean {
  return tokenFromCookie === tokenFromBody;
}

七、本章小结

通过本章学习,你应该掌握了:

  • Auth.js 的完整配置与使用方法
  • OAuth 社交登录与邮箱密码登录的实现
  • Session 在服务端和客户端组件中的获取方式
  • Middleware 路由保护与 RBAC 权限控制
  • JWT 手动实现的底层原理与安全存储
  • 常见安全陷阱及防护策略(XSS、CSRF、暴力破解)

下一章《从原理到实践深度剖析缓存策略》将继续更深入地剖析 Next.js 的多层缓存架构,揭示其工作原理、最佳实践以及常见陷阱。

Next.js中间件鉴权绕过漏洞CVE-2025-29927深度解析与修复指南 Web应用安全中的鉴权机制是保障数据隐私和访问控制的核心环节,其原理在于验证用户身份并授权访问特定资源。在技术实现上,中间件作为请求处理管道的关键组件,常在边缘计算环境中执行全局安全策略。对于采用Next.js等现代框架的应用,中间件鉴权提供了高效的路由保护方案,其技术价值在于统一处理认证逻辑并提升开发效率。然而,CVE-2025-29927漏洞暴露了Next.js中间件在特定请求路径和重定向逻辑处理中的安全缺陷,可能导致鉴权绕过风险。该漏洞涉及中间件执行链的边界条件问题,在特定配置下重定向响应可能失效,使 阅读详情

相关推荐

next-authentication:Next.js框架的身份验证和授权库

下次认证 Next.js的身份验证和授权 next-authentication提供了一组功能和中间件,以在Next.js应用程序中实现身份验证,授权和会话管理。 用法 建立: // Setup // file: lib/auth.js import bcrypt from 'bcrypt' ; import { nextAuth , AuthError } from 'next-authentication' ; import { User } from '../user/model' ; const nextAuthOptions = { cookieName : 'auth

在 Next.js 中实现用户授权

OAuth2.0 是一种行业标准的授权协议,使互联网用户能够与第三方网站和应用程序共享其帐户信息,而无需提供其帐户凭据。本指南使用 NextAuth.js 库在 Next.js 应用程序中实现用户授权 (OAuth2.0)。NextAuth.js 是用于 Next.js 的成熟的身份验证和授权解决方案 ,旨在与任何 OAuth 服务一起使用。它内置了对许多流行登录服务的支持,包括 Google 和 GitHub。要学习本教程,您需要 Node.js 12.0 或更高版本 以及 React 的基本知识。

分享身边生活经验blog 4411

next-js-authentication:Next.js身份验证应用

具有Next-Auth的Next.js身份验证应用程序

Next.js12和Next.js13的区别

用了一年多的Next.js,经历了Next.js12到Next.js13的迭代,简单总结一下用后感

旺旺旺小小苏的博客 998

blogr-nextjs-prisma:使用NextJS和NextAuth进行身份验证

使用NextJS和NextAuth进行身份验证 看板 库: NextJS 棱柱 PostgreSQL Heroku 打字稿 Vercel NextAuth NextJS API路由 指令 启动应用程序: yarn dev 要在localhost:5555打开Prisma Studio,请执行以下操作: npx prisma studio 要在数据库中创建表,请执行以下操作: npx prisma db push --preview-feature 要实现模式更改: npx prisma generate

Chapter2、HDR流水线

HDR内容的获取(不同曝光时间的图片合成) 相机响应函数 HDR视频

qq_55621259的博客 2597

Next.js 常见鉴权方案

本文介绍了Next.js(App Router)中三种常见的鉴权方案:基于Cookie/Session的网站登录方案、基于JWT的前后端分离方案和基于API Key的服务端调用方案。重点演示了JWT认证的实现过程,包括登录接口生成Token、保护API路由进行Token校验等关键步骤。通过代码示例展示了如何使用jsonwebtoken和bcryptjs库实现用户认证,并说明了如何在校验通过后返回受保护的用户数据。最后强调访问受保护API时需在请求头中携带有效的JWT Token。

尽日寻春不见春,芒鞋踏遍陇头云。归来笑拈梅花嗅,春在枝头已十分。 667

分析android逆向的学习路线

俗话说:“一入逆向深似海,从此头发是路人”。 安卓逆向作为当下热门行业“网络安全”的主要技术之一,已经受到了越来越多人的青睐。但是很多刚入门的新手小伙伴对于怎么去学习却还很是迷糊。本文就分享一下android逆向的学习路线供大家参考。 目前学习安卓逆向的人群大约分为两种: 第一种是完全没有任何基础的计算机纯小白; 第二种是本来就做开发懂编程的IT行业人员。 我们就针对这两个不同的人群做一下安卓逆向的学习路线分析。 一、无基础小白想入门 作为没有任何基础想要入门的计算机纯小白,学习上推荐参照以下步骤: (一

Hacker_by_V的博客 3078

【Next.js 入门教程系列】07-身份验证

【Next.js 入门教程系列】07-身份验证。本篇包括以下内容: 设置 Next Auth、设置 the Google Provider、Authentication sessions、保护路由、数据库适配器

Castamere的博客 2161

基于NXP i.MX RT1180的无传感器FOC电机驱动实战指南

磁场定向控制(FOC)是一种先进的电机控制技术,它通过精密的数学变换,将三相交流电机的控制解耦为独立的转矩和磁场分量,从而实现高效、平稳、低噪音的驱动。其核心原理在于利用Park/Clark变换,在旋转坐标系下对电流进行线性控制。这项技术的价值在于显著提升了永磁同步电机(PMSM)的效率与动态性能,广泛应用于机器人、无人机和工业自动化等高精度场景。本文聚焦于利用NXP i.MX RT1180跨界处理器及其MCUXpresso SDK,快速实现一套完整的无传感器FOC解决方案。通过解析硬件平台选型、软件架构,

weixin_33925615的博客 304

解锁Jetbrains AI助手:中国开发者实战配置与效率跃迁指南

本文为中国开发者提供了一份详细的Jetbrains AI助手实战配置指南。针对服务地域限制,核心解决方案是通过修改Jetbrains账户区域信息并配置网络代理,实现插件的稳定启用。文章进一步分享了AI助手在解读遗留代码、生成中文提交信息、智能问答等场景下的具体应用,旨在帮助开发者深度集成这一工具,实现编码生产力的显著提升。

weixin_28832121的博客 576

【项目亮点】Next.js结合Supabase鉴权:打造无缝用户认证体验

【项目亮点】Next.js结合Supabase鉴权:打造无缝用户认证体验 在当今快速发展的Web应用领域,安全且流畅的用户认证体验成为每个开发者关注的核心。今天,我们深入探讨一个精彩的开源项目——Next.js + Supabase Auth,它巧妙地将前沿的Next.js框架与强大的Supabase身份验证系统相结合,为你的应用程序提供了一套简洁高效的认证解决方案。 项目介绍 Next.js +...

gitblog_00569的博客 1449

Visual Studio Code ADT Extension 能不能连接 ECC,答案是可以,但真正的门槛在 ABAP 后端版本

Visual Studio Code 版 ABAP Development Tools(ADT)支持连接传统 SAP ECC 系统,关键不在于系统名称,而在于底层 SAP NetWeaver AS ABAP 版本是否达到最低要求(7.31 SP04 及以上)。只要系统已支持 Eclipse ADT,即可无缝使用 VS Code ADT,无需升级后端。SAP 通过复用原有 ADT 技术栈,将兼容性逻辑置于客户端,实现对老系统的持续支持。尽管初期侧重 ABAP Cloud 模型,但 1.1 及后续版本已全面覆盖

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 51

Next.js中间件鉴权绕过漏洞剖析与安全加固实战

在Web应用开发中,中间件是实现统一身份验证和访问控制的核心机制,它通过在请求处理链的早期执行逻辑来保护后端资源。其工作原理是拦截匹配的HTTP请求,进行认证状态检查、权限验证或请求修改,然后决定放行或拦截。这一机制的技术价值在于为应用提供了可复用、集中式的安全层,是构建企业级应用、内部管理系统和SaaS平台的关键组件。然而,如果配置不当,例如路径匹配规则与路由重写不同步,或对静态资源路径、非标准HTTP方法的处理存在疏漏,就可能引入严重的安全风险,导致鉴权逻辑被绕过。本文以一次典型的Next.js中间件鉴

anjueci1221的博客 430

知识图谱-KGE-语义匹配-双线性模型-2018:SimplE

【paper】 SimplE Embedding for Link Prediction in Knowledge Graphs 【简介】 本文是加拿大英属哥伦比亚大学的两位学者发表在 NIPS 2018 上的工作,文章提出了 SimplE(Simple Embedding)。这篇和前面一篇差不多,也是对 1927 年的 CP 进行改进(简化),传统CP为实体分配的两个向量是独立训练的,SimplE利用关系的逆在三元组打分函数中加上了一个对称项,使得每个实体的两个向量依赖学习,并且 SimplE 的复杂度随

u013250861的博客 958

HyperLynx(三十一)高速串行总线仿真(三)

(5)选择 “File”→ “Open Touchstone”,打开路径 C:\Adv_HL_Tmg\SERDES_Interface\models,选中connector.s4p,单击打开,如图所示。接下来,找出将要分配模型的S参数连接。(12)单击“Assign”按钮,选择模型类型为“Touchstone models”,在库中选择“connector.s4p”,如图所示。(14)单击“OK”按钮,选中“connector.s4p”这一行,单击“Connection Editor”按钮,如图所示。

小幽余生不加糖 3367

Next.js 15 + MySQL + NextAuth.js 基于 Cookie/Session 鉴权实战

本文介绍了使用Next.js 15和MySQL实现基于NextAuth的用户认证系统的完整流程。主要内容包括:环境配置(Node.js 18+、Next.js 15、MySQL)、数据库设计(创建用户表)、数据库连接设置、NextAuth配置(使用JWT策略和CredentialsProvider)、用户注册API实现、登录和注册页面开发。文章提供了详细的代码示例,涵盖了从数据库操作到前端交互的完整认证流程实现,并强调了NextAuth v5的新特性。通过.env文件配置敏感信息,确保开发安全性。该系统支持

尽日寻春不见春,芒鞋踏遍陇头云。归来笑拈梅花嗅,春在枝头已十分。 860

智能车图像处理19-进阶篇11--斜入十字补线

这篇文章主要讲述智能车竞赛中如何对于斜入十字进行补线操作。

taiyuezyh的博客 7488

全栈鉴权实战:基于JWT与Node.js/Vue.js构建安全认证系统

在现代Web应用开发中,用户认证与授权是保障数据安全和实现用户隔离的核心机制。其基本原理是通过一套规则和流程,确认用户身份并控制其访问权限。JWT作为一种无状态令牌技术,因其适合分布式架构、易于扩展的特性,成为前后端分离应用的主流鉴权方案。该技术通过编码用户信息并签名,实现了安全、高效的身份验证,为构建可扩展的Web服务提供了重要价值。在实际工程实践中,结合Node.js后端与Vue.js前端,可以系统性地实现从用户注册登录、令牌签发验证,到请求拦截与路由守卫的完整鉴权链路。本文以“章鱼哥解题”项目为例,详

weixin_30873847的博客 368

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

如何用cookies-next在Next.js中间件中安全处理Cookie:终极指南

在Next.js应用中安全处理Cookie是每个开发者必须掌握的技能!🚀 今天我将为你介绍如何使用**cookies-next**这个强大的库在Next.js中间件中进行安全Cookie管理,让你的应用既高效又安全。 ## 什么是cookies-next? **cookies-next**是一个专门为Next.js设计的Cookie管理库,它能够在客户端和服务器端无缝工作,支持Next.js

gitblog_00234的博客 993

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

TVBoxOSC 新手指南:电视盒子 3 步装好,MKV 电影与直播频道直接播

当盒子自带播放器打不开下载好的 MKV,或字幕显示成一堆乱码方块时,可以试试 TVBoxOSC。这款免费开源的电视盒子播放器,本地影片、外挂字幕、M3U 直播频道都能直接接上,不用先做格式转换。 ## 这款播放器适合谁,又劝退谁 把片子存进 U 盘或 NAS、想在大屏上看,或者想在盒子上挂一份频道列表的人,正合适。它按电视遥控器的使用习惯优化,字大、导航直接,老型号盒子也能稳定运行。如果你要的

gitblog_00374的博客 136

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Next.js API路由JWT鉴权与异常处理实战指南

JWT(JSON Web Token)作为现代Web开发中广泛采用的无状态认证方案,其核心原理是通过Header、Payload和Signature三部分实现安全数据传输。在Next.js全栈框架中,结合HS256加密算法可以实现高效的API鉴权体系。通过环境变量管理密钥、设置合理有效期等安全实践,能有效防御CSRF、XSS等常见攻击。在电商等高并发场景下,配合Redis缓存验证结果可显著提升性能,实测可将鉴权耗时控制在8ms内。本文以c-shopping项目为例,详细演示了如何实现多角色RBAC控制、Jo

weixin_30856725的博客 410

OpenCore Legacy Patcher 完整教程:免费给老 Mac 装最新 macOS 全流程

OpenCore Legacy Patcher(简称 OCLP)是一款免费开源的 macOS 补丁工具,专门让苹果官方已放弃支持的老款 Intel Mac 重新安装 macOS Big Sur 及以后版本。工具覆盖 2007 年以后的绝大多数机型,能一次性补齐显卡加速、Wi-Fi、蓝牙等驱动。本文从动手前的体检,到克隆仓库启动、制作安装 U 盘、写入引导、完成装机和打根补丁,最后再讲一遍长期更新回

gitblog_00122的博客 370

WorkOS AuthKit Next.js 中间件强制认证机制解析

WorkOS AuthKit Next.js 中间件强制认证机制解析 在构建现代Web应用时,认证机制的安全性至关重要。WorkOS AuthKit Next.js库近期引入了一项关键功能改进,解决了开发者在使用中间件进行认证时可能遇到的安全隐患。 传统认证模式的痛点 在传统的Next.js应用中,开发者通常需要在每个需要认证的页面或API路由中显式调用getUser()方法来验证用户身份。这种模...

gitblog_07586的博客 967

Next.js认证实战:NextAuth.js + PostgreSQL全栈鉴权架构

现代Web应用的认证已超越简单登录逻辑,本质是跨客户端、服务端与数据库三重上下文的信任体系构建。其核心原理在于会话状态的安全流转与权限策略的分层控制,技术价值体现在可扩展性、安全合规性及多租户支持能力。典型应用场景包括SaaS后台、企业级管理平台及需OAuth+2FA+RBAC的生产系统。PostgreSQL凭借JSONB、行级安全(RLS)和强事务一致性,成为NextAuth.js生产部署的事实底座;而NextAuth.js则通过Adapter机制将认证逻辑与数据契约深度解耦。本文聚焦于二者协同落地的关键

weixin_33250328的博客 291

Next.js DApp 中间件层:钱包认证、路由守卫与 RPC 故障转移

Next.js DApp 的中间件层应从三方面设计:RPC 故障转移(多节点轮询 + 健康检查 + 重试)、钱包路由守卫(客户端守卫 + 链切换检测 + 重定向防抖)、Context 注入(统一向下传递 wallet 和 provider 状态)。核心原则是"关注点分离":页面组件只处理业务,横切关注点由中间件统一管理。RPC 故障转移需增加区块哈希交叉验证防止数据脑裂;钱包守卫需处理用户拒绝链切换的状态。写操作的故障转移比读操作复杂,需要额外考虑交易广播的非幂等性。

qq_40635035的博客 3072
上一篇: Next.js从入门到实战保姆级教程:错误处理与加载状态
下一篇: Next.js从入门到实战保姆级教程:性能优化深度实践
FrontAI
博客等级 码龄1年 61粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值