NodeJS - 如何使用Express创建和读取会话

55

我希望在用户进入应用程序时创建一个用户会话,并在需要的时候读取该会话。这是我的尝试:

var io   = require('socket.io'),
    express = require('express');
    querystring = require('querystring');

var app = express.createServer();
app.get('/', function(req, res){
    var sessionVal = querystring.parse(req.url.substr(2));// sessionVal is an email for example: me@gmail.com
    app.use(express.cookieParser());
    app.use(express.session({ secret: sessionVal }));
});
var socket = io.listen(app);
socket.on('connection', function(client) {
    client.on('message', function(message) {
        // message will be an object {text:'user text chat blah blah', email:'me@gmail.com'}
        // if the seesion stored, has the same value with message.email
        // then the message will be broadcasted
            socket.broadcast(message.text);
        // else will not broadcast  
    });
});

app.listen(4000);
5个回答

80

需要指出的是,您正在错误地将中间件添加到应用程序中。 app.use 调用不应在 app.get 请求处理程序内部完成,而应在其外部直接调用它们,或者参考文档中的其他示例

传递给 express.session 的密钥应该是一个字符串常量,或者可能来自配置文件。不要传递客户端可能知道的内容,这实际上是危险的。这是仅服务器应该知道的秘密。

如果您想将电子邮件地址存储在会话中,只需执行类似以下方式的操作:

req.session.email = req.param('email');

先说一下,如果我理解正确的话,你想要做的是处理一个或多个HTTP请求并跟踪一个会话,然后稍后从中打开一个Socket.IO连接,其中你需要会话数据。

这个问题的棘手之处在于,Socket.IO 使魔法在任何 http.Server 上生效的方法是通过劫持 request 事件。因此,Express(或者更确切地说是Connect)的会话中间件永远不会被 Socket.IO 连接调用。

我相信你可以通过一些巧妙的方法让它工作起来。

你可以获取 Connect 的会话数据;你只需要获取一个对会话存储的引用即可。最简单的方法是在调用 express.session 之前自己创建存储。






















// A MemoryStore is the default, but you probably want something
// more robust for production use.
var store = new express.session.MemoryStore;
app.use(express.session({ secret: 'whatever', store: store }));
每个会话存储都有一个get(sid, callback)方法。 sid参数,或称为会话ID,存储在客户端的cookie中。该cookie的默认名称为connect.sid。(但是您可以通过在express.session调用中指定key选项来更改任何名称。)
然后,您需要在Socket.IO连接中访问该cookie。不幸的是,Socket.IO似乎没有提供对http.ServerRequest的访问。简单的解决方法是在浏览器中获取cookie,并将其通过Socket.IO连接发送。
服务器上的代码看起来如下:
var io      = require('socket.io'),
    express = require('express');

var app    = express.createServer(),
    socket = io.listen(app),
    store  = new express.session.MemoryStore;
app.use(express.cookieParser());
app.use(express.session({ secret: 'something', store: store }));

app.get('/', function(req, res) {
  var old = req.session.email;
  req.session.email = req.param('email');

  res.header('Content-Type', 'text/plain');
  res.send("Email was '" + old + "', now is '" + req.session.email + "'.");
});

socket.on('connection', function(client) {
  // We declare that the first message contains the SID.
  // This is where we handle the first message.
  client.once('message', function(sid) {
    store.get(sid, function(err, session) {
      if (err || !session) {
        // Do some error handling, bail.
        return;
      }

      // Any messages following are your chat messages.
      client.on('message', function(message) {
        if (message.email === session.email) {
          socket.broadcast(message.text);
        }
      });
    });
  });
});

app.listen(4000);

假设您只想读取现有会话。 您实际上无法创建或删除会话,因为Socket.IO连接可能没有要发送Set-Cookie标头的HTTP响应(考虑WebSockets)。

如果您想编辑会话,则可以尝试在某些会话存储中使用。例如,CookieStore将无法工作,因为它还需要发送Set-Cookie标头,但是对于其他存储,您可以尝试调用set(sid, data, callback)方法,然后查看发生了什么。


感谢您提供的详细解释,我使用了 req.session.email = req.param('email'); 语句。请问我该如何访问存储的电子邮件值? - angry kiwi
我忘了告诉一个错误,当我使用req.session.email = req.param('email')时,服务器报错说无法设置未定义的属性email。 - angry kiwi
我仍然相信你错误地使用了中间件。当正确使用时,你应该有一个 req.session。正如我在示例中展示的那样,你可以使用 req.session.email 访问存储的电子邮件。 - Stéphan Kochen
@Shtééf我们能否通过它的值而不是ID来获取会话? - angry kiwi
2
默认情况下,cookie 被设置为 HttpOnly。express.session 还有一个 cookie 选项。请参阅:http://senchalabs.github.com/connect/middleware-session.html - Stéphan Kochen
显示剩余6条评论

5

当我使用req.session.email = req.param('email')时,我忘记了告诉一个bug,服务器错误显示无法设置未定义的属性电子邮件。

这个错误的原因是app.use的顺序不正确。你必须按照以下顺序配置express:

app.use(express.cookieParser());
app.use(express.session({ secret: sessionVal }));
app.use(app.route);

5

在socket.io和connect sessions支持之间进行互操作是繁琐的。这个问题不是因为socket.io在某种程度上“劫持”了请求,而是因为某些socket.io传输协议(我认为是flashsockets)不支持cookie。我的解决方法如下:

  1. 为socket.io实现一个单独的会话存储,以与connect-redis相同的格式存储数据
  2. 使连接会话cookie不是http-only,以便可以从客户端JS访问
  3. 在socket.io连接时,将会话cookie从浏览器发送到服务器
  4. 将会话ID存储在socket.io连接中,并使用它来从redis访问会话数据。

2

我所做的步骤:

  1. Include the angular-cookies.js file in the HTML!
  2. Init cookies as being NOT http-only in server-side app.'s:

    app.configure(function(){
       //a bunch of stuff
       app.use(express.cookieSession({secret: 'mySecret', store: store, cookie: cookieSettings}));```
    
  3. Then in client-side services.jss I put ['ngCookies'] in like this:

    angular.module('swrp', ['ngCookies']).//etc

  4. Then in controller.js, in my function UserLoginCtrl, I have $cookies in there with $scope at the top like so:

    function UserLoginCtrl($scope, $cookies, socket) {

  5. Lastly, to get the value of a cookie inside the controller function I did:

    var mySession = $cookies['connect.sess'];

现在你可以从客户端将其发送回服务器。太棒了。但愿他们在Angular.js文档中放入这个功能。我只是直接阅读angular-cookies.js的实际代码才明白它的用法。

0

你好,我正在尝试在Node.js中添加新的会话值,例如:

req.session.portal = false
Passport.authenticate('facebook', (req, res, next) => {
    next()
})(req, res, next)

关于护照策略,我在Mozilla请求中无法获取门户值,但在Chrome和Opera中正常工作。

FacebookStrategy: new PassportFacebook.Strategy({
    clientID: Configuration.SocialChannel.Facebook.AppId,
    clientSecret: Configuration.SocialChannel.Facebook.AppSecret,
    callbackURL: Configuration.SocialChannel.Facebook.CallbackURL,
    profileFields: Configuration.SocialChannel.Facebook.Fields,
    scope: Configuration.SocialChannel.Facebook.Scope,
    passReqToCallback: true
}, (req, accessToken, refreshToken, profile, done) => {
    console.log(JSON.stringify(req.session));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接