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

Express.js中HTTP-only Cookie前端无法接收的问题求助

Express.js JWT+HTTP-only Cookie认证:前端登录无Cookie,Postman正常

问题描述

我用Express.js结合JWT令牌和HTTP-only Cookie实现后台管理面板的认证功能。当前端发送登录请求时,响应状态显示成功,但浏览器未收到Cookie;用Postman发送相同请求时,Cookie能正常返回。后续调用/checkToken端点时,控制台输出“Authentication token missing”,浏览器Application面板里也看不到任何Cookie。


前端登录提交代码

const handleSubmit = async (e) => {
    e.preventDefault();
    if (loading) return;
    setLoading(true); // 开始加载

    try {
        const response = await axios.post(
            `${import.meta.env.VITE_BACKEND_URI}/adminLogin`,
            { Id: id, Password: password }, { withCredentials:true }
        );

        if (response.status === 200) {
            navigate('/Home');
        }
    } catch (error) {
        setError(error.response?.data?.error || "登录失败,请检查网络或稍后重试。");
    } finally {setLoading(false);}
};

后端AdminLogin路由代码

import express from 'express';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();
const router = express.Router();

router.post('/', (req, res) => {
    try {
        const { Id, Password } = req.body;

        console.log(process.env.ADMIN_ID === String(Id));
        console.log(process.env.ADMIN_PASSWORD === String(Password));
        
        if (process.env.ADMIN_ID === String(Id) && process.env.ADMIN_PASSWORD === String(Password)) {
            const token = jwt.sign({ id: Id, password: Password }, process.env.JWT_SECRET, { expiresIn: '1h' });
            
            res.cookie("jwtToken", token, {
                httpOnly: true,
                secure: process.env.NODE_ENV === 'production', 
                sameSite: "Lax",
                maxAge: 3600 * 1000,
                path: '/', 
            });
            
            console.log("correct");
            res.sendStatus(200);
        } else {
            res.status(401).json({ error: "无效的凭据" });
        }
    } catch (error) {
        console.log(error);
        res.status(500).json({ error: "内部服务器错误" });
    }
});

export default router;

server.js配置代码

import express from "express";
import bodyParser from "body-parser";
import cookieParser from 'cookie-parser';
import cors from "cors";
import dotenv from "dotenv";
import connectDB from "./config/db.js";
import loginRoute from "./routes/Login.js";
import signUpRoute from "./routes/SignUp.js";
import authorizationRoute from "./routes/Authorize.js";
import deleteUserRoute from "./routes/deleteUser.js";
import sendEmailRoute from "./routes/Subscriber.js";
import sendInquiriesRoute from "./routes/Inquiries.js";
import adminLoginRoute from "./routes/AdminLogin.js";
import tokenRoute from "./middleware/auth.js";

dotenv.config();
connectDB();

const server = express();
const port = process.env.PORT_NO || 3000;

server.use(bodyParser.json());
server.use(cookieParser());

server.use(cors({
    origin: ["http://localhost:4000", "http://localhost:5173"],
    credentials: true,
}));

// 首页路由
server.get('/', (req, res) => {
    res.send("this is home route");
});

// 注册路由
server.use('/login', loginRoute);
server.use('/signUp', signUpRoute);
server.use('/authorizeEmail', authorizationRoute);
server.use('/deleteUser', deleteUserRoute);
server.use('/sendEmail', sendEmailRoute);
server.use('/sendInquiries', sendInquiriesRoute);
server.use('/adminLogin', adminLoginRoute);
server.use("/checkToken", tokenRoute);

server.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

checkToken验证端点代码

import express from 'express';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();

const router = express.Router();

router.get('/', (req, res) => {
    const token = req.cookies.jwtToken;
    console.log(token)
    
    if (!token) {
        return res.status(401).json({ error: "认证令牌缺失" });
    }

    jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
        if (err) {
            return res.status(403).json({ error: "令牌无效或已过期" });
        }

        return res.sendStatus(200)
    });
});

export default router;

排查与解决步骤

  1. 确认前端origin在CORS允许列表中
    检查import.meta.env.VITE_BACKEND_URI对应的前端运行地址是否是http://localhost:4000或http://localhost:5173,如果前端实际端口不同,需添加到CORS的origin数组中。

  2. 调整Cookie的sameSite属性
    跨端口属于跨域场景,可尝试区分环境设置sameSite:开发环境设为"None"(需配合secure: false,因为localhost是HTTP协议),生产环境保留"Lax"。修改后的Cookie配置:

    res.cookie("jwtToken", token, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: process.env.NODE_ENV === 'production' ? "Lax" : "None",
        maxAge: 3600 * 1000,
        path: '/',
    });
    
  3. 确保所有请求携带凭据
    后续调用/checkToken等需要验证的接口时,同样要给axios请求加上{ withCredentials: true }配置。

  4. 检查浏览器隐私设置
    部分浏览器默认阻止第三方Cookie,可临时关闭该选项测试是否能正常接收Cookie。

  5. 查看响应头的Set-Cookie字段
    在浏览器Network标签中查看登录请求的响应头,若存在Set-Cookie但浏览器未保存,说明Cookie属性不符合跨域要求。

内容的提问来源于stack exchange,提问作者Muhammad Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:40:57