在Node.js中使用Express.js,会话(Session)是如何工作的?

105
使用Express.js,会话非常简单。但我很好奇它是如何工作的。
它是否在客户端存储某些cookie?如果是这样,我该在哪里找到那个cookie?如果需要,我该如何解码它?
基本上,我想要能够查看用户是否已登录,即使用户此时并不在站点上(就像Facebook知道您在其他站点上登录时一样)。但我认为要理解这一点,我首先应该了解会话是如何工作的。
4个回答

181

概述

Express.js使用一个cookie在用户的浏览器中存储一个带有加密签名的会话ID,随后,在后续的请求中,使用该cookie的值来检索存储在服务器上的会话信息。这个服务器端的存储可以是内存存储(默认)或任何其他实现必需方法的存储(例如connect-redis)。

详情

Express.js/Connect创建一个24位字符的Base64字符串,使用utils.uid(24)并将其存储在req.sessionID中。然后将该字符串用作cookie中的值。

客户端

始终为会话使用已签名的cookie,因此cookie值将具有以下格式。

[sid].[signature]

其中[sid]为sessionID,[signature]是使用初始化会话中间件时提供的密钥对[sid]进行签名生成的。 签名步骤用于防止篡改。如果没有使用密钥,则修改[sid]并重新创建[signature]应该在计算上是不可行的。如果无需修改[sid],则会话cookie仍然容易被盗窃和重复使用。

此cookie的名称为

connect.sid

服务器端

如果一个处理程序在cookieParsersession中间件之后发生,它将可以访问变量req.cookies。这个变量包含一个JSON对象,其中的键是cookie的键,值是cookie的值。它将包含一个名为connect.sid的键,它的值将是已签名的会话标识符。

下面是一个设置路由的示例,它将在每个请求上检查会话cookie的存在并将其值打印到控制台。

app.get("/*", function(req, res, next) {

    if(typeof req.cookies['connect.sid'] !== 'undefined') {
        console.log(req.cookies['connect.sid']);
    }

    next(); // Call the next middleware
});

在配置部分,确保把路由器(app.use(app.router))放在cookieParsersession之后。

以下是Express.js/Connect内部存储的数据示例。

{
  "lastAccess": 1343846924959,
  "cookie": {
    "originalMaxAge": 172800000,
    "expires": "2012-08-03T18:48:45.144Z",
    "httpOnly": true,
    "path": "/"
  },
  "user": { 
    "name":"waylon",
    "status":"pro"
  }
}

user 字段是自定义的,其他的都是会话管理的一部分。

这个例子来自 Express 2.5 版本。


1
当使用console.log打印cookie值时,它会给出编码(签名)的cookie。我该如何获取真实信息? - vsync
什么是“如果不需要修改[sid],会话cookie仍然容易被盗用和重复利用”的意思?由于SID是由Express生成的,我们永远无法更改它,对吗? - Hrushikesh
2
@WebHrushi 我在考虑中间人攻击:http://en.wikipedia.org/wiki/Man-in-the-middle_attack - Waylon Flinn
有人能帮我回答这个问题吗:http://stackoverflow.com/questions/21982791/opening-password-protected-site-passport-js-with-phantom-js-by-setting-cookie - roundrobin
我如何为任何特定的session_id创建一个令牌? - aman verma

46

我从未使用过Express.js,虽然根据他们的文档,听起来:

  • cookie 存储在客户端,带有一个键(服务器将用于检索会话数据)和一个哈希值(服务器将用于确保 cookie 数据没有被篡改,因此如果您尝试更改值,则 cookie 将无效)。

  • 与某些框架(例如Play Framework!)不同,会话数据保存在服务器上,因此 cookie 更像是会话的占位符而不是实际会话数据的持有者。

  • 根据这里,看起来默认情况下服务器上的会话数据保存在内存中,但可以更改为实现适当 API 的任何存储形式。

因此,如果要在没有特定 req 请求对象的情况下检查事物,就像您所说的那样,您需要访问相同的存储。在第一页的底部,详细介绍了存储需要实现的必需方法,因此如果您熟悉存储 API,则可以执行类似 .getAll() 的操作(如果存在此类内容),并循环浏览会话数据并读取所需的任何值。


9
我很好奇它们究竟是如何工作的。
试着看看this的答案和wiki的内容。
它通常会存储一些分配的会话ID的cookie,应该使用密钥进行签名以防止伪造。
你不应该在客户端处理会话cookie。如果你想在服务器端处理会话,你应该查看相关的express.jsconnect文档。

3

除了已经出色的答案外,以下是我创建的两个图表,用于解释Express会话、它们与Cookie的关系和存储:

  • 巧克力饼干: chocolate
  • 草莓饼干: strawberry

图表的解释:

每个Cookie都有一个唯一的“味道”(或sessionId)。当草莓曲奇饼干被提交给服务器(在HTTP请求中),服务器识别这个味道并从存储器加载相应的数据:Rosie的数据,并将其填充到req.session中。


抱歉,我从图表中没有完全理解任何信息。您想要传达什么意思? - Rafael Sofi-zada
2
@RafaelSofi-zada 每个 cookie 都有一个独特的“口味”(或 sessionId)。当草莓饼干被提交到服务器(通过 HTTP 请求),服务器会识别这种口味并从存储中加载相应的数据:Rosie 的数据,并填充 req.session - abernier

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