最近我部署了节点/express api 和客户端(Nextjs 14.1.0)来 vercel 除了 cookie 之外一切都工作正常。在某些情况下,我会收到 cookie,但它们只是临时的,当我刷新页面时,所有内容都消失了,而在其他情况下,我会收到有关域的警告(如下)。
“通过 Set-Cookie 设置 cookie 的尝试被阻止,因为其域属性无效”
即使我在cookie中设置了域名,如下所示,但仍然没有成功
res.cookie("refreshToken", refreshToken, {
maxAge: 15 * 24 * 60 * 60 * 1000,
sameSite: "none",
httpOnly: true,
secure: true,
partitioned: true,
domain: ".mern-nextjs-ecommerce.vercel.app"
});
res.cookie("accessToken", accessToken, {
maxAge: 15 * 24 * 60 * 60 * 1000,
sameSite: "none",
httpOnly: true,
secure: true,
partitioned: true,
domain: ".mern-nextjs-ecommerce.vercel.app"
});
登录码
export const signIn = TryCatch(async (req, res, next) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) return next(new ErrorHandler("Invalid credentials", 400));
const isMatched = await user.comparePassword(password);
if (!isMatched) return next(new ErrorHandler("Invalid credentials", 400));
const accessToken = jwt.sign({ id: user._id }, config.JWT_SECRET, {
expiresIn: "45m",
});
const refreshToken = jwt.sign({ id: user._id }, config.JWT_SECRET);
if (!user.tokens) user.tokens = [refreshToken];
else user.tokens.push(refreshToken);
await user.save();
res.cookie("refreshToken", refreshToken, {
maxAge: 15 * 24 * 60 * 60 * 1000,
sameSite: "none",
httpOnly: true,
secure: true,
partitioned: true,
});
res.cookie("accessToken", accessToken, {
maxAge: 15 * 24 * 60 * 60 * 1000,
sameSite: "none",
httpOnly: true,
secure: true,
partitioned: true,
});
res.json({
success: true,
profile: {
id: user._id,
email: user.email,
name: user.username,
verified: user.verified,
avatar: user.avatar?.url,
accessToken: accessToken,
},
accessToken,
refreshToken,
});
});
Cors 配置
app.use(
cors({
origin: [
"https://mern-nextjs-ecommerce.vercel.app",
config.ADMIN_CLIENT_URL,
"http://localhost:3000",
],
credentials: true,
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: [
"Content-Type",
"Authorization",
"Origin",
"X-Requested-With",
"Accept",
"Set-Cookie",
"Cookie",
],
})
);
前端提交功能
const handleSignInSubmit = async (data: SignInInput) => {
try {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/signin`, {
method: "POST",
credentials: "include", // This ensures cookies are sent with the request
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: data.email,
password: data.password,
}),
});
const resData = await res.json();
toast.success(resData.message || "Logged In !");
} catch (error) {
toast.error("An error occurred");
}
};
中间件.ts
import { NextRequest, NextResponse } from "next/server";
export async function middleware(request: NextRequest) {
const accessToken = request.cookies.get("accessToken")?.value;
const refreshToken = request.cookies.get("refreshToken")?.value;
console.log("accessToken: ", accessToken);
console.log("refreshToken: ", refreshToken);
const response = NextResponse.next();
if (!accessToken || !refreshToken) return response;
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/me`, {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
},
credentials: "include",
});
if (res.status === 401) {
try {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}auth/refresh-token`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
refreshToken: refreshToken,
}),
credentials: "include",
}
);
const data = await res.json();
if (data.success) {
const response = NextResponse.redirect(request.url);
response.cookies.set("accessToken", data.tokens.access, {
httpOnly: true,
sameSite: "lax",
});
response.cookies.set("refreshToken", data.tokens.refresh, {
httpOnly: true,
sameSite: "lax",
});
return response;
}
} catch (error) {
console.log("An error occurred11111: ", error);
}
}
return response;
}
我建议您根据 NextRequest 的主机名动态设置域。如果您在 Vercel 上进行部署,这将帮助您更轻松地处理预览链接和开发/生产部署