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

Next.js middleware校验JWT抛出无法解析crypto模块错误

问题根因

Next.js 12.2版本稳定后的Middleware默认运行在Edge Runtime环境,该运行时基于Web标准API构建,不兼容Node.js原生crypto模块。jsonwebtoken库的底层依赖jwa、jws均强绑定Node.js原生crypto实现JWT签名、校验逻辑,因此只要在Middleware中调用jwt.verify方法,就会触发Module not found: Can't resolve 'crypto'报错,和路由重定向逻辑本身无关——注释掉verify调用后逻辑正常运行,也能印证这一点。

可行解决方案

按推荐优先级从高到低排列:

  • 替换为Edge Runtime原生兼容的JWT库
    直接卸载jsonwebtoken,改用基于Web Crypto API实现的jose库,这是Next.js官方推荐在Edge场景使用的JWT工具,无Node原生模块依赖,可直接在Middleware中正常运行。
    1. 安装依赖:
    npm install jose
    # 用yarn/pnpm的话,对应执行yarn add jose / pnpm add jose即可
    
    1. 改造Middleware鉴权逻辑:
    // middleware.ts
    import { NextResponse } from 'next/server'
    import type { NextRequest } from 'next/server'
    import { jwtVerify } from 'jose'
    
    // jose要求密钥转换为Uint8Array格式,直接从环境变量读取即可
    const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET)
    
    export async function middleware(request: NextRequest) {
      // 仅拦截/dashboard路径下的所有路由
      if (request.nextUrl.pathname.startsWith('/dashboard')) {
        const token = request.cookies.get('token')?.value
        // 无token直接跳登录
        if (!token) {
          return NextResponse.redirect(new URL('/login', request.url))
        }
        try {
          // 校验JWT签名、有效期是否合法
          await jwtVerify(token, JWT_SECRET)
          return NextResponse.next()
        } catch {
          // token无效、过期、被篡改均跳登录
          return NextResponse.redirect(new URL('/login', request.url))
        }
      }
      return NextResponse.next()
    }
    
    // 配置匹配规则,避免Middleware拦截不必要的静态资源等请求
    export const config = {
      matcher: '/dashboard/:path*'
    }
    
    1. 同步改造登录接口的JWT签发逻辑,和原有业务逻辑完全对齐即可:
    // /pages/api/auth/login.ts
    import { SignJWT } from 'jose'
    import type { NextApiRequest, NextApiResponse } from 'next'
    
    const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET)
    
    export default async function handler(req: NextApiRequest, res: NextApiResponse) {
      // 保留原有账号密码校验逻辑,校验通过后签发JWT
      // const user = await validateAccount(req.body.username, req.body.password)
      // if (!user) return res.status(401).json({ message: '账号或密码错误' })
    
      const token = await new SignJWT({
        userId: user.id,
        username: user.username
        // 其余需要写入payload的字段和之前保持一致
      })
        .setProtectedHeader({ alg: 'HS256' })
        .setExpirationTime('7d') // 替换为实际需要的有效期
        .sign(JWT_SECRET)
    
      // 原有写cookie、返回响应的逻辑不需要改动
      res.setHeader('Set-Cookie', `token=${token}; Path=/; HttpOnly; Max-Age=604800; SameSite=Lax`)
      return res.status(200).json({ success: true })
    }
    
  • 拆分鉴权逻辑,不在Middleware层做签名校验
    如果不想替换现有jsonwebtoken相关代码,可以保留Middleware中判断token是否存在的逻辑,把JWT有效性校验下沉到Node.js运行时层处理:比如在getServerSideProps、服务端组件、API路由中用jsonwebtoken的verify方法做校验,无效则返回重定向响应。
    该方案的缺点是无效token的请求会穿透Edge层到达Node服务,相比Edge层直接拦截会有额外的性能损耗,但是不需要改动现有JWT相关的业务代码。
  • 强制Middleware运行在Node.js Runtime(不推荐)
    Next.js支持手动配置Middleware的运行时,在middleware.ts文件顶部添加如下配置即可让代码跑在Node环境,原生支持crypto模块:
    export const runtime = 'nodejs'
    
    该方案会丢失Edge Runtime冷启动快、可边缘部署的优势,且部分边缘部署平台对Node.js Runtime的Middleware存在兼容限制,生产环境不优先推荐。
避坑说明

不要尝试通过polyfill的方式给Edge Runtime补全crypto模块:JWT校验涉及密码学操作,第三方polyfill普遍存在实现不完整、体积过大、安全漏洞等问题,无法满足生产环境要求。

内容的提问来源于stack exchange,提问作者Philipos D.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:33:32