在 Next.js v13 中,我尝试使用身份验证,并且想将 JWT 令牌存储在 cookie 中,我能够存储它,但无法检索它,我尝试使用多个库(例如: cookie、js-cookie、cookies-next)甚至尝试使用“next/headers”中的 cookie,但仍然无法在服务器组件和客户端组件中检索它。
“应用程序/api/auth/loginUser/route.js”
import User from "@/app/_models/User";
import connectDB from "@/app/lib/connectdb";
import { AES, enc } from "crypto-js";
import { NextResponse } from "next/server";
import { sign } from "jsonwebtoken";
import { serialize } from "cookie";
const MAX_AGE = 60 * 60 * 24 * 2;
export async function POST(request) {
try {
connectDB();
const { email, password } = await request.json();
const user = await User.findOne({ email: email });
if (!user)
return new NextResponse(
JSON.stringify({
success: false,
message: "Invalid credentials",
})
);
const dycPassword = AES.decrypt(
user.password,
process.env.CRYPT_SECRET_KEY
).toString(enc.Utf8);
if (dycPassword !== password) {
return new NextResponse(
JSON.stringify({
success: false,
message: "Invalid credentials",
})
);
}
const token = sign(user.toObject(), process.env.JWT_SECRET_KEY, {
expiresIn: "2d",
});
const setCookie = serialize("userToken", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
maxAge: MAX_AGE,
path: "/",
httpOnly: false,
});
return new NextResponse(
JSON.stringify({
success: true,
message: "Welcome!!!",
}),
{ status: 200, headers: { "Set-Cookie": setCookie } }
);
} catch (error) {
return new NextResponse(
JSON.stringify({ success: false, message: error.message }),
{ status: 500 }
);
}
}
“应用程序/api/auth/me/route.js”
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
export async function POST(req) {
try {
const cookieStore = cookies();
const token = cookieStore.get("userToken");
console.log(cookieStore.getAll(), token);
return new NextResponse(
JSON.stringify({ success: true, token: token }, { status: 200 })
);
} catch (error) {
return new NextResponse(
JSON.stringify({ success: false, message: error.message }),
{ status: 500 }
);
}
}
“应用程序/page.jsx”
import React from "react";
import { cookies } from "next/headers";
export const dynamic = "force-static";
export default async function Page() {
const token = cookies().get("userToken");
const data = await fetch("http://localhost:3000/api/auth/me", {
method: "POST",
}).then((data) => data.json());
console.log(data);
return (
<div>
<ul>{token}</ul>
</div>
);
}
上面的代码可以存储cookie,但无法检索它。我从 console.logs 得到的都是空对象、数组或未定义
依赖列表:
"dependencies": {
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"cookie": "^0.5.0",
"crypto-js": "^4.1.1",
"jsonwebtoken": "^9.0.2",
"mongoose": "^7.5.3",
"next": "latest",
"next-themes": "^0.2.1",
"react": "latest",
"react-dom": "latest",
"react-switch": "^7.0.0",
"react-toastify": "^9.1.3",
"sass": "^1.68.0"
},
"devDependencies": {
"autoprefixer": "latest",
"eslint": "latest",
"eslint-config-next": "latest",
"postcss": "latest",
"tailwindcss": "latest"
}
更新:
所以我正在尝试标头,结果标头没有发送到我需要 cookie 的页面,当我使用 ThunderClient (VS 代码扩展)发出请求时,我能够获取我设置的 cookie ThunderClient 但在像 chrome 和 Edge 这样的浏览器中,标头只是空的,它们没有 cookie 或任何东西,所以要检查我是否创建了一个中间件和 console.log 标头,所以在中间件中所有的东西都存在,但是,它们没有被发送到我需要它们的服务器组件(“app/page.js”)。
中间件.js
import { NextResponse } from "next/server";
// This function can be marked `async` if using `await` inside
export async function middleware(request) {
console.log(request.headers.get("cookie"));
return NextResponse.next({
request: {
headers: new Headers(request.headers),
},
});
}
// See "Matching Paths" below to learn more
export const config = {
matcher: "/",
};
我在“app/page.js”中获得的标题:
HeadersList {
cookies: null,
[Symbol(headers map)]: Map(0) {},
[Symbol(headers map sorted)]: null
}
我在“app/api/auth/me/route.js”中获得的标头:
HeadersList {
cookies: null,
[Symbol(headers map)]: Map(10) {
'accept' => { name: 'accept', value: '*/*' },
'accept-encoding' => { name: 'accept-encoding', value: 'gzip, deflate' },
'accept-language' => { name: 'accept-language', value: '*' },
'cache-control' => { name: 'cache-control', value: 'no-cache' },
'connection' => { name: 'connection', value: 'keep-alive' },
'content-length' => { name: 'content-length', value: '0' },
'host' => { name: 'host', value: 'localhost:3000' },
'pragma' => { name: 'pragma', value: 'no-cache' },
'sec-fetch-mode' => { name: 'sec-fetch-mode', value: 'cors' },
'user-agent' => { name: 'user-agent', value: 'undici' }
},
[Symbol(headers map sorted)]: [
[ 'accept', '*/*' ],
[ 'accept-encoding', 'gzip, deflate' ],
[ 'accept-language', '*' ],
[ 'cache-control', 'no-cache' ],
[ 'connection', 'keep-alive' ],
[ 'content-length', '0' ],
[ 'host', 'localhost:3000' ],
[ 'pragma', 'no-cache' ],
[ 'sec-fetch-mode', 'cors' ],
[ 'user-agent', 'undici' ]
]
}
另外,这就是我重定向到主页的方式:
const handleSubmit = async (e) => {
e.preventDefault();
if (email == "" || password == "") {
toast.error("Fill all the fields");
return;
}
const res = await fetch("api/auth/loginUser", {
method: "POST",
cache: "no-store",
body: JSON.stringify({ email, password }),
}).then((data) => data.json());
if (res.success) {
toast.success(res.message);
router.push("/"); // <-------------redirect
} else {
toast.error(res.message);
}
};
您不能同时使用
export const dynamic = 'force-static'
和cookies()。根据此 NextJS 文档页面:
通过强制
、cookies()
和headers()
返回空值来强制静态渲染并缓存布局或页面的数据。useSearchParams()
唯一的解决方案是更改方法(也许使用
sessionStorage
来存储令牌?)或渲染为与 force-static
不同的东西。虽然我很清楚 force-static
在加载时间方面具有很大的优势(因为页面是完全静态的并且可以缓存),但是 headers()
、cookies()
等的任何使用都是毫无意义的,因为空值将是回来了。