You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止Next.js API接口被未授权访问?

解决办法:限制Next.js API仅允许自有域名访问

针对你的需求——让访客无需登录就能查看作品集,但禁止外部网站直接调用API,以下是几个可行方案:

方案1:校验请求的Referer/Origin头

浏览器发起请求时会自动带上Referer(来源页面)或Origin(来源域名)头,我们可以在API路由里检查这些头是否属于你的域名,以此判断请求是否合法。

修改app/api/portfolio/route.js:

import { headers, NextResponse } from 'next/server';
import { connectDB, getPortfolioItems } from './path-to-your-utils'; // 替换成你的工具文件路径

export const GET = async (request) => {
  // 允许的域名列表,包含生产环境和本地开发环境
  const allowedDomains = [
    'https://codingsamadhan.com',
    'http://codingsamadhan.com',
    'http://localhost:3000'
  ];

  const origin = headers().get('origin');
  const referer = headers().get('referer');
  
  // 检查Origin或Referer是否匹配允许的域名
  const isAllowed = allowedDomains.some(domain => {
    if (origin) return origin.startsWith(domain);
    if (referer) return referer.startsWith(domain);
    return false;
  });

  if (!isAllowed) {
    return NextResponse.json({ message: "Unauthorized Access!" }, { status: 403 });
  }

  await connectDB();
  const portfolioItems = await getPortfolioItems(request);
  return NextResponse.json(portfolioItems);
};

注意点

  • 同域请求可能没有Origin头,所以要结合Referer一起校验
  • 虽然这些头可以被伪造,但足以阻止绝大多数普通的外部API调用,完全符合你的需求
  • 本地开发环境必须加入允许列表,不然开发时会被拦截

方案2:用Next.js Middleware统一拦截

如果你有多个API路由需要保护,用Middleware可以一次性处理所有API请求,不用在每个路由里重复写校验代码。

在项目根目录创建middleware.js:

import { NextResponse } from 'next/server';

export function middleware(request) {
  const allowedDomains = [
    'https://codingsamadhan.com',
    'http://codingsamadhan.com',
    'http://localhost:3000'
  ];

  const origin = request.headers.get('origin');
  const referer = request.headers.get('referer');

  const isAllowed = allowedDomains.some(domain => {
    if (origin) return origin.startsWith(domain);
    if (referer) return referer.startsWith(domain);
    return false;
  });

  // 只拦截/api开头的请求
  if (request.nextUrl.pathname.startsWith('/api/') && !isAllowed) {
    return NextResponse.json({ message: "Unauthorized Access!" }, { status: 403 });
  }

  return NextResponse.next();
}

// 指定要拦截的路径规则
export const config = {
  matcher: '/api/:path*',
};

关于next-auth/JWT无需登录的方案

如果你一定要用next-auth,其实可以配置匿名会话,让未登录用户自动获得一个合法会话,然后在API里校验这个会话:

  1. 安装next-auth:npm install next-auth
  2. 创建app/api/auth/[...nextauth]/route.js:
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: "Anonymous",
      credentials: {}, // 不需要用户输入凭证
      async authorize() {
        // 返回一个匿名用户对象,用于生成会话
        return { id: "anonymous", name: "Anonymous Visitor" };
      },
    }),
  ],
  session: { strategy: "jwt" }, // 用JWT存储会话
  secret: process.env.NEXTAUTH_SECRET, // 从环境变量获取密钥
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
  1. 修改API路由校验会话:
import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]/route";
import { connectDB, getPortfolioItems } from './path-to-your-utils';

export const GET = async (request) => {
  const session = await getServerSession(authOptions);
  
  // 只要存在会话(不管是否是匿名)就允许访问
  if (!session) {
    return NextResponse.json({ message: "Unauthorized Access!" }, { status: 403 });
  }

  await connectDB();
  const portfolioItems = await getPortfolioItems(request);
  return NextResponse.json(portfolioItems);
};
  1. 在前端页面自动初始化匿名会话:
import { signIn } from "next-auth/react";
import { useEffect } from "react";

export default function PortfolioPage() {
  useEffect(() => {
    // 页面加载时自动登录匿名用户,无需跳转
    signIn("credentials", { redirect: false });
  }, []);

  // 页面内容...
}

不过这个方案相对复杂,其实前面的Referer/Origin校验已经完全能满足你的需求,而且更轻量,没必要多此一举。

为什么X-Api-Key方案没用

前端代码里的任何密钥都会在浏览器的网络面板里暴露,所以后端不能依赖前端传过来的密钥做鉴权——这种方式只适合服务器之间的调用,不适合浏览器发起的请求,你的判断是对的。

内容的提问来源于stack exchange,提问作者Md Injamul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:24:50