如何处理服务器连续请求发送两个不同的cookie?

How to deal with the server sending two different cookies for consecutive requests?

当我在同一页面上发出两个同步连续提取请求时,服务器会为该客户端发出两个不同的 session。这是因为第二个请求在 之前发送到服务器,第一个响应将 Set-Cookie header 发送回客户端。

控制台显示两个请求都没有发送cookie。因此,这两个响应都创建了一个新的 session.

-----------'/first' request------------
'req.headers.cookie: undefined
'req.sessionID: uZCc-2EtuOIdqD5xfcBCBLQjZbRmy29k
'/first' Set-Cookie header: connect.sid=s%3AuZCc-2EtuOIdqD...

-----------'/second' request------------
'req.headers.cookie: undefined
'req.sessionID: Gzr5CPYvE5deGA-Ul7EwOzHbaWFtpXR3
'/second' Set-Cookie header: connect.sid=s%3AGzr5CPYvE5deGA... 

您可以进一步看出这个问题是由于您取消注释 // await addNSecondsDelay(5);(记住清除要复制的 cookie)以创建 5 秒延迟时第二个请求触发得太早造成的。这将只创建一个 session.

我不希望每次新客户访问具有多个请求的页面时都创建一个额外的未使用 session。我可以想出几种方法来解决这个问题——比如异步调用 fetch——但这似乎不适合更复杂的情况,也许 html 页面是动态创建的,你不能将 fetch 调用链接在一起对于每个动态生成的组件。这似乎也会减慢页面加载速度。

是否有正确的方法来确保不创建两个 session?

后端服务器

// other-server.js
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');

const app = express();
const sess = {
  saveUninitialized: false,
  resave: false,
  secret: 'very secret 12345',
  cookie: {
    sameSite: true,
    secure: false,
  },
};

app.use(bodyParser.json());
app.use(
  bodyParser.urlencoded({
    extended: true,
  })
);
app.use(session(sess));

app.use((req, res, next) => {
  console.log(`\n-----------'${req.path}' request------------`);

  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Accept, Content-Type');
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5001');
  res.setHeader('Access-Control-Allow-Credentials', true);
  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH'
  );
  res.on('close', function () {
    console.log(
      `'${req.path}' Set-Cookie header: ${res.getHeaders()['set-cookie'][0].slice(0, 30)}...`
    );
  });
  req.session.a = 'hi';
  console.log(`'req.headers.cookie: ${req.headers.cookie}`);
  console.log(`'req.sessionID: ${req.sessionID}`);
  next();
});
app.post('/first', (req, res) => {
  res.json({ message: "1st fetch's response" });
});
app.post('/second', (req, res) => {
  res.json({ message: "2nd fetch's response" });
});

app.listen(4001, () => {
  console.log('start');
});

前端服务器

// server.js
const express = require('express');

const app = express();
app.use(express.static('./public'));
app.listen(5001);

index.html

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const addNSecondsDelay = (n) => {
        console.log('5 second delay ...');
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve();
          }, n * 1000);
        });
      };

      const asyncFunctionCall = async () => {
        fetch('http://localhost:4001/first', {
          method: 'POST',
          credentials: 'include',
        })
          .then((response) => {
            return response.json();
          })
          .then((json) => {
            console.log('Received:  ' + JSON.stringify(json));
          });
        // await addNSecondsDelay(5);
        fetch('http://localhost:4001/second', {
          method: 'POST',
          credentials: 'include',
        })
          .then((response) => {
            return response.json();
          })
          .then((json) => {
            console.log('Received:  ' + JSON.stringify(json));
          });
      };

      asyncFunctionCall();
    </script>
  </body>
</html>

这些是您的选择:

  1. 在执行任何其他操作之前发送 1 个请求,这样您就可以得到一个 cookie。
  2. 让 2 个 cookie 无关紧要。

您收到的最后 Set-Cookie header 'wins',并且没有好的方法可以知道这 2 个请求是否真的来自同一个客户端(这就是 cookie 的用途) ,因此您无法直接轻松解决此问题。

我能想到的唯一其他选择是让客户端自己生成一些唯一的 ID 并将其与两个请求一起发送,但我怀疑就安全性而言,这也是一种蠕虫病毒。

理想的解决方案是将 运行 API 与加载网页的服务器放在同一台服务器上?如果是这样,那么将网页加载到浏览器的行为本身就已经在网页 运行 中的任何 Javascript 之前在浏览器中设置了会话 cookie。