Phi*_* D. 3 middleware node.js jwt next.js
我正在尝试使用 JWT 验证通过中间件进行身份验证,但不幸的是,我遇到了一些无法找到解决方案的错误。
./node_modules/jwa/index.js:3:0
Module not found: Can't resolve 'crypto'
Import trace for requested module:
./node_modules/jws/lib/sign-stream.js
./node_modules/jws/index.js
./node_modules/jsonwebtoken/verify.js
./middleware.ts
https://nextjs.org/docs/messages/module-not-found
You're using a Node.js module (crypto) which is not supported in the Edge Runtime.
Learn more: https://nextjs.org/docs/api-reference/edge-runtime
Run Code Online (Sandbox Code Playgroud)
包.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@emotion/react": "^11.9.3",
"@emotion/styled": "^11.9.3",
"@mui/material": "^5.8.6",
"@prisma/client": "^4.0.0",
"axios": "^0.27.2",
"buffer": "^6.0.3",
"cookie": "^0.5.0",
"jsonwebtoken": "^8.5.1",
"next": "12.2.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.33.0"
},
"devDependencies": {
"@types/node": "18.0.0",
"@types/react": "18.0.14",
"@types/react-dom": "18.0.5",
"eslint": "8.18.0",
"eslint-config-next": "12.2.0",
"prisma": "^4.0.0",
"typescript": "4.7.4"
}
}
Run Code Online (Sandbox Code Playgroud)
中间件.ts
import { NextResponse } from "next/server";
import verify from "jsonwebtoken/verify";
import { urlToHttpOptions } from "url";
import type { NextRequest } from 'next/server'
const secret = process.env.SECRET;
export default function middleware(req: NextRequest) {
const { cookies } = req;
const { search, protocol, host } = req.nextUrl
const jwt = cookies.OutsiteJWT;
const url = req.url;
if (url.includes('/dashboard')) {
if (jwt === undefined) {
return NextResponse.redirect("http://localhost:3000/login");
}
try {
verify(jwt, secret); // <---- ERROR COMES FROM HERE
return NextResponse.next();
} catch (error) {
return NextResponse.redirect("/login");
}
}
return NextResponse.next();
}
Run Code Online (Sandbox Code Playgroud)
/pages/api/auth/login.ts
import { NextResponse } from "next/server";
import verify from "jsonwebtoken/verify";
import { urlToHttpOptions } from "url";
import type { NextRequest } from 'next/server'
const secret = process.env.SECRET;
export default function middleware(req: NextRequest) {
const { cookies } = req;
const { search, protocol, host } = req.nextUrl
const jwt = cookies.OutsiteJWT;
const url = req.url;
if (url.includes('/dashboard')) {
if (jwt === undefined) {
return NextResponse.redirect("http://localhost:3000/login");
}
try {
verify(jwt, secret); // <---- ERROR COMES FROM HERE
return NextResponse.next();
} catch (error) {
return NextResponse.redirect("/login");
}
}
return NextResponse.next();
}
Run Code Online (Sandbox Code Playgroud)
我尝试注释掉try/catch来检查令牌是否已设置,并且它工作正常,但是当我尝试在中间件中验证时,它失败了。
在版本 12.2.0 中,中间件很稳定,但也有一些变化。
其他人是否遇到类似的问题或知道如何解决这个问题?
根据与 GitHub 中用户的讨论,显然jose库在中间件中运行 Edge 函数时效果更好,而jsonwebtoken则不然。
/中间件.ts
import { NextResponse } from "next/server";
import { urlToHttpOptions } from "url";
import type { NextRequest } from 'next/server'
import { verify } from "./services/jwt_sign_verify";
const secret = process.env.SECRET || "secret";
export default async function middleware(req: NextRequest) {
const jwt = req.cookies.get("OutsiteJWT");
const url = req.url;
const {pathname} = req.nextUrl;
if (pathname.startsWith("/dashboard")) {
if (jwt === undefined) {
req.nextUrl.pathname = "/login";
return NextResponse.redirect(req.nextUrl);
}
try {
await verify(jwt, secret);
return NextResponse.next();
} catch (error) {
req.nextUrl.pathname = "/login";
return NextResponse.redirect(req.nextUrl);
}
}
return NextResponse.next();
}
Run Code Online (Sandbox Code Playgroud)
/services/jwt_sign_verify.ts
import { SignJWT, jwtVerify, type JWTPayload } from 'jose';
import { Token } from "@typescript-eslint/types/dist/generated/ast-spec";
export async function sign(payload: string, secret: string): Promise<string> {
const iat = Math.floor(Date.now() / 1000);
const exp = iat + 60 * 60; // one hour
return new SignJWT({ payload })
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
.setExpirationTime(exp)
.setIssuedAt(iat)
.setNotBefore(iat)
.sign(new TextEncoder().encode(secret));
}
export async function verify(token: string, secret: string): Promise<JWTPayload> {
const { payload } = await jwtVerify(token, new TextEncoder().encode(secret));
// run some checks on the returned payload, perhaps you expect some specific values
// if its all good, return it, or perhaps just return a boolean
return payload;
}
Run Code Online (Sandbox Code Playgroud)
/pages/api/auth/login.ts
/* eslint-disable import/no-anonymous-default-export */
import { serialize } from "cookie";
import { sign } from "../../../services/jwt_sign_verify";
const secret = process.env.SECRET || "secret";
export default async function (req, res) {
const { username, password } = req.body;
// Check in the database
if (username === "Admin" && password === "Admin") {
const token = await sign(
"testing",
secret
);
const serialised = serialize("OursiteJWT", token, {
httpOnly: true,
secure: process.env.NODE_ENV !== "development",
sameSite: "strict",
maxAge: 60 * 60 * 24 * 30,
path: "/",
});
res.setHeader("Set-Cookie", serialised);
res.status(200).json({ message: "Success!" });
} else {
res.status(401).json({ message: "Invalid credentials!" });
}
}
Run Code Online (Sandbox Code Playgroud)