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

Next.js 14中间件中执行SQL查询验证JWT以实现安全路由保护的问题

Next.js 14中间件中执行SQL查询验证JWT以实现安全路由保护的问题

兄弟我太懂你这烦恼了!刚上手Next.js 14做认证路由的时候,我也卡在中间件不能直接连数据库这事儿上——你现在的代码确实完全没安全性,随便整个token cookie就能绕过去,得赶紧改。

首先得说清楚为啥中间件里不能直接用Prisma:Prisma客户端的初始化逻辑和边缘环境(Next.js中间件跑在Edge Runtime里)不太兼容,尤其是本地数据库,根本没法直接建立连接。那怎么解决?给你两个靠谱的方案:

方案一:把验证逻辑移到API路由,中间件调用API做校验

这是最稳妥的办法,本地开发和生产环境都能用。思路就是:把查数据库、验证JWT的逻辑放到一个内部API路由里,中间件只负责把token传给这个API,根据返回结果决定放不放行。

第一步:写验证token的API路由

在app/api/validate-token/route.ts里写这个逻辑:

import { NextResponse } from 'next/server';
import prisma from '@/lib/prisma'; // 你的Prisma实例路径
import jwt from 'jsonwebtoken';

export async function POST(request: Request) {
  const { token } = await request.json();

  if (!token) {
    return NextResponse.json({ valid: false }, { status: 400 });
  }

  // 先查数据库里有没有这个token
  const user = await prisma.user.findFirst({
    where: { jwtToken: token }, // 假设你的user表有存token的字段
  });

  if (!user) {
    return NextResponse.json({ valid: false }, { status: 401 });
  }

  // 再验证JWT本身的签名和过期时间,这步非常重要!
  try {
    jwt.verify(token, process.env.JWT_SECRET!);
    return NextResponse.json({ valid: true }, { status: 200 });
  } catch (err) {
    // 签名无效或者token过期
    return NextResponse.json({ valid: false }, { status: 401 });
  }
}

第二步:修改中间件代码

现在中间件里不再直接碰数据库,而是调用上面的API:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const tokenCookie = request.cookies.get('token');

  // 先检查有没有token cookie
  if (!tokenCookie) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    // 调用内部API验证token
    const validateResponse = await fetch(
      new URL('/api/validate-token', request.url),
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token: tokenCookie.value }),
        cache: 'no-store', // 禁止缓存,每次都查最新状态
      }
    );

    const { valid } = await validateResponse.json();

    if (!valid) {
      // token无效的话,清掉cookie再重定向到登录页
      const redirectResponse = NextResponse.redirect(
        new URL('/login', request.url)
      );
      redirectResponse.cookies.delete('token');
      return redirectResponse;
    }

    // 验证通过,放行
    return NextResponse.next();
  } catch (err) {
    // 网络错误或者其他异常,直接打回登录页
    return NextResponse.redirect(new URL('/login', request.url));
  }
}

export const config = {
  matcher: '/home',
};

方案二:用Prisma的Edge版本(仅适合生产托管数据库)

如果你用的是托管数据库(比如PlanetScale、Supabase),可以试试Prisma的Edge客户端版本,这样就能在中间件里直接用Prisma了。不过本地开发的话,本地数据库还是没法和Edge Runtime兼容,所以还是方案一更实用。

额外注意事项

  • 一定要验证JWT本身:不能只查数据库里有没有这个token,还要用jsonwebtoken库验证签名和过期时间,防止token过期或者被篡改。
  • 环境变量存密钥:JWT的密钥一定要存在.env里,绝对不能硬编码到代码里。
  • 可以加缓存优化:如果同一个token短时间内多次请求,可以在API路由里加个内存缓存(比如用lru-cache),避免重复查数据库。
  • 中间件fetch用内部URL:别用外部域名,就用/api/xxx这种内部路径,避免跨域和性能问题。

备注:内容来源于stack exchange,提问作者jeyremd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 08:58:09