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

React应用收到Set-Cookie响应头但未保存Cookie问题求助

登录会话Cookie未保存问题

我搭建了一套简易登录系统,用户提交用户名和密码后,React前端会将数据POST至Express后端。输入正确数据时响应始终成功,但调用登录POST接口时,会话已创建且Cookie已返回至React应用,但该Cookie未被浏览器保存。


React端登录函数代码

async function login(event){
    event.preventDefault();
    setRequestState(REQUEST_STATE.pending)

    const request = `${SERVER_URL}/login`;
    const formData = new FormData();
    
    formData.append("username",data["username"])
    formData.append("password",data["password"])

    const response = await fetch(request, {
        method: 'POST',
        body: formData,
        enctype:"multipart/form-data",
        credentials: 'same-origin',
    })

    if (!response.ok) {
        if (response.status === 401) {
            console.log("username or password are incorrect")
            setDisplayError(response.status)
        }

        if (response.status === 500) { 
            console.log("internal server error")
            setDisplayError(response.status)
        }

        setRequestState(REQUEST_STATE.no_request)

        return;
    }

    setRequestState(REQUEST_STATE.success)
    // navigate("/dashboard")
}

Express会话配置代码

const express = require('express');
const cors = require('cors')
const loginRouter = require('./src/routes/login.js');
const session = require('express-session')

const app = express()

app.use(
    cors({
        origin:true,
        credentials:'same-origin',
}))

app.use(session({
  secret: 'test',
  resave: false,
  saveUninitialized: false,
//   store: sessionStore,
  cookie: { 
    secure: false,
    sameSite: 'none',
}
}));


app.use(express.urlencoded({extended: true}))


app.use('/login', loginRouter)

app.listen(process.env.PORT ,()=>{
    console.log(`Server is listening on port ${process.env.PORT}`)
})

登录路由代码

const express = require('express')
const multer = require('multer')
const router = express.Router()
const {getLogin,postLogin, authenticateLogin} = require('../controllers/login')

upload = multer({})

router.post("/", upload.array(0), authenticateLogin, postLogin)
router.get("/", getLogin)

module.exports=router

登录控制器代码

const {pool} = require('../../sqlconfig')
const bcrypt = require('bcrypt');

const postLogin = (req, res) => {
    return res.status(200).send({success:true});
}

const getLogin = (req, res) => {
    // console.log("get this login betch")
    console.log(req.session.id)
    if(req.session.userId){
        return res.status(200).send({success:true});
    }
    // console.log("couldn't get login")
    return res.status(401).send({success:false});
}

const authenticateLogin = async (req, res, next) => {

    const dict = JSON.parse(JSON.stringify(req.body));
    
    const username = dict["username"];
    const password = dict["password"];

    if(!dict || !username || !password) {
        // console.log("Access deneid")
        return res.status(401).send({error:"Access denied"});
    }
    try{
        const query = "SELECT passwrd FROM admins WHERE admins.username=?";
        const [result] = await pool.execute(query,[username])

        if(result.length <= 0){
            return res.status(401).send({error:"Access denied"});
        }

        const passwordInDB = result[0].passwrd;

        if(!passwordInDB){
            return res.status(401).send({error:"Access denied"});
        }
        
        const match = await bcrypt.compare(password, passwordInDB);
        // console.log(match)

        if(!match){
            return res.status(401).send({error:"Access denied"});
        }
        
        const queryUserId = "SELECT id FROM admins WHERE admins.username=?";
        const [resultUserId] = await pool.execute(queryUserId,[username])
        
        req.session.userId = resultUserId[0].id;
        console.log("logged in successfuly!")
        console.log(req.session)
        
        next()
    }
    catch(e){
        console.log(e)
    }
}

module.exports={
    authenticateLogin,
    postLogin,
    getLogin
}

问题排查与修复步骤

1. 调整前端Fetch的Credentials配置

将React登录函数中fetch的credentials: 'same-origin'改为credentials: 'include',跨域场景下浏览器才会处理Cookie的保存与携带:

const response = await fetch(request, {
    method: 'POST',
    body: formData,
    credentials: 'include', // 修改此处
})

注:enctype不是fetch的合法配置项,可直接移除,FormData默认采用multipart/form-data编码。

2. 修正后端CORS配置

将cors的credentials值改为true,同时建议明确指定前端域名(避免模糊匹配):

app.use(
    cors({
        origin: 'http://localhost:3000', // 替换为你的前端实际域名
        credentials: true, // 修改为true
    })
)

3. 调整Session Cookie的SameSite与Secure配置

HTTP环境(本地开发)下,sameSite: 'none'会被浏览器拒绝(该属性要求Cookie必须为Secure,即HTTPS环境),需改为'lax'或'strict':

app.use(session({
  secret: 'test',
  resave: false,
  saveUninitialized: false,
  cookie: { 
    secure: false, // HTTP环境设为false,HTTPS环境改为true
    sameSite: 'lax', // 修改为lax或strict
  }
}));

若为HTTPS生产环境,可保持sameSite: 'none'并设置secure: true。

4. 简化FormData解析逻辑

无需通过JSON.parse(JSON.stringify(req.body))转换,直接从req.body获取参数即可:

const authenticateLogin = async (req, res, next) => {
    const username = req.body.username;
    const password = req.body.password;

    if(!username || !password) {
        return res.status(401).send({error:"Access denied"});
    }
    // 后续代码不变
}

内容的提问来源于stack exchange,提问作者Amit Ritter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 07:50:25