【问题标题】:Express session resets session on every request快速会话在每个请求上重置会话
【发布时间】:2021-05-30 10:35:48
【问题描述】:

我有一个 VueJS 项目,它使用 axios 调用另一个域上的服务器。在这台服务器上,我需要在会话中保存一些值,这样就不需要在每个请求中查找它们。

服务器是 NodeJS,在 Heroku 上运行,我使用 Redis 进行内存存储。我可以成功地将数据保存到会话中,但是对于每个新请求,系统都会使用新 ID 创建一个新会话,因此我无法访问上一个请求期间保存的值。

编辑 根据一些建议更新代码后,我可以在会话 cookie 上的网络控制台中看到以下错误:

Preflight Invalid Allow Credentials

编辑 2 我能够通过向 corsOptions 添加“credentials:true”来解决 Preflight Invalid Allow Credentials。这解决了我在会话中在网络中看到的错误,但我仍然为每个请求获得一个新的会话 ID。

服务器上的代码:

const express = require('express');
const app = express();

const cors = require('cors');
var corsWhitelist = ['http://127.0.0.1:8080','http://127.0.0.1:8081']
var corsOptions = {
  origin: function (origin, callback) {
    if (corsWhitelist.indexOf(origin) !== -1) {
        callback(null, true)
    } else {
        callback(new Error('Not allowed by CORS - '+origin))
    }
  },
credentials: true
}

let REDIS_URL = process.env.REDIS_URL;
var Redis = require('ioredis');

const session = require('express-session');
const cookieParser = require('cookie-parser');
const RedisStore = require('connect-redis')(session);
const sessionClient = new Redis(REDIS_URL)

sessionClient.on('error', function (err) {
    console.log('could not establish a connection with redis. ' + err);
  });
sessionClient.on('connect', function (err) {
    console.log('connected to redis successfully');
  });

app.set('trust proxy', 1)
app.use(cookieParser());
app.use(session({
    store: new RedisStore({ client: sessionClient }),
    secret: 'someSecret',
    resave: false,
    saveUninitialized: true,
    cookie: {
        secure: false,
        httpOnly: false,
        maxAge: 1000 * 60 * 10
    }
}))

app.use(cors(corsOptions));
app.options('*', cors(corsOptions))
// Add headers
app.use(function (req, res, next) {
    if (corsWhitelist.indexOf(req.headers.origin) !== -1) {
        res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
        res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
        res.setHeader('Access-Control-Allow-Credentials', 'true');
    }
    next();
});

const getUser = async function(req, res, next) {
    if (!req.session.user) {
        req.session.user = "test@example.com"
        req.session.save()
    }
    next()
  }

app.get('/session', getUser, (req, res) => {
    // get the session id
    console.log('session id:', req.session.id)
    // the session will be automatically stored in Redis with the key prefix 'sess:'
    const sessionKey = `sess:${req.session.id}`;
    // let's see what is in there
    client.get(sessionKey, (err, data) => {
      console.log('session data in redis:', data)
    })
    res.status(200).send('OK');
  })

VueJS 上的方法:

getSession: async function () { 
  axios({
    url: 'https://server.example.com/session',
    withCredentials: true,
  }).then(res => {
    console.log(res)
  })
},

【问题讨论】:

  • 可能与 cookie 有关。见Express session not saving/finding session
  • 在 Chrome 调试器的网络选项卡中,您可以观察 axios 调用,看看它是否正在发送会话 cookie 与请求,这就是为什么 express-session 总是创建一个新会话。
  • 我在发布之前确实看过那个答案(实际上是因为它解决了我的部分问题),但我不确定如何让 axios 调用发送 cookie。
  • 您是否在 Chrome 调试器的网络选项卡中确认它没有发送 cookie?如果是这样,请尝试为 axios 添加withCredentials: true 选项。
  • 是的,cookie 在那里,但是当我添加 withCredentials 时,它会出现 CORS 错误。我尝试了几种方法来解决它,但无法弄清楚。我更新了问题和代码。

标签: node.js session heroku redis cors


【解决方案1】:

需要进行一些更改才能使其正常工作:

预检设置被设置了两次,所以在下面的代码中,我需要删除第二行:

app.use(cors(corsOptions));
app.options('*', cors(corsOptions)) //delete this

我试图在“// 添加标头”下设置的标头没有进入预检请求,因此我需要将“credentials: true”添加到 corsOptions 并删除“// 添加”下的代码标题”:

var corsOptions = {
  origin: function (origin, callback) {
    if (corsWhitelist.indexOf(origin) !== -1) {
        callback(null, true)
    } else {
        callback(new Error('Not allowed by CORS - '+origin))
    }
  },
  credentials: true
}

最后但同样重要的是,会话定义中的 cookie 设置不适用于跨域请求。具体来说,“sameSite: 'none'”和“secure: true”是必要的。结果如下所示:

app.use(session({
    store: new RedisStore({ client: client }),
    secret: 'someSecret',
    resave: false,
    saveUninitialized: true,
    cookie: {
        secure: true,
        httpOnly: false,
        sameSite: 'none',
        maxAge: 1000 * 60 * 10
    }
}))

【讨论】:

    猜你喜欢
    • 2020-07-13
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    • 2015-06-10
    • 2023-03-12
    • 2020-07-01
    相关资源
    最近更新 更多