这是 Mozilla 身份团队的《Node.JS 假期季系列》中总共 12 集的第 3 集。它涵盖了如何为可扩展的 Node.js 应用程序使用会话。
静态网站易于扩展。您可以对其进行大量缓存,并且无需在向最终用户提供此内容的各种服务器之间传播状态。
不幸的是,大多数 Web 应用程序需要保存一些状态才能为用户提供个性化体验。如果用户可以登录您的网站,那么您需要为他们保留会话。通常的做法是使用随机会话标识符设置 cookie,并在服务器上根据此标识符存储会话详细信息。
扩展有状态服务
现在,如果您想扩展该服务,基本上有三种选择
- 在所有 Web 服务器上复制该会话数据,
- 使用每个 Web 服务器都连接到的中央存储,或
- 确保给定用户始终访问同一 Web 服务器
这些都有缺点
- 复制会带来性能成本并增加复杂性。
- 中央存储会限制扩展并增加延迟。
- 将用户限制在特定服务器会导致问题,当该
服务器需要关闭时。
但是,如果您从另一个角度看待问题,则可以找到第四种选择:在客户端存储会话数据。
客户端会话
将会话数据推送到浏览器有一些明显的优势
- 数据始终可用,无论哪个机器为用户提供服务
- 服务器上没有要管理的状态
- Web 服务器之间无需复制任何内容
- 可以立即添加新的 Web 服务器
不过,存在一个关键问题:您无法信任客户端不会篡改会话数据。
例如,如果您在 cookie 中存储用户帐户的用户 ID,则该用户可以轻松更改该 ID,然后访问其他人的帐户。
虽然这听起来像是一个障碍,但有一种巧妙的解决方案可以解决此信任问题:将会话数据存储在防篡改包中。这样,就不需要相信用户没有修改会话数据。服务器可以对其进行验证。
在实践中,这意味着您使用服务器密钥对 cookie 进行加密和签名,以防止用户读取或修改会话数据。这就是 client-sessions 的作用。
node-client-sessions
如果您使用 Node.JS,则可以使用一个库,该库使开始使用客户端会话变得非常简单:node-client-sessions。它替换了 Connect 的内置 session 和 cookieParser 中间件。
以下是如何将其添加到 简单的 Express 应用程序 中
const clientSessions = require("client-sessions");
app.use(clientSessions({
secret: '0GBlJZ9EKBt2Zbi2flRPvztczCewBxXK' // set this to a long random string!
}));
然后,您可以像这样在 req.session
对象上设置属性
app.get('/login', function (req, res){
req.session.username = 'JohnDoe';
});
并将其读回
app.get('/', function (req, res){
res.send('Welcome ' + req.session.username);
});
要终止会话,请使用 reset 函数
app.get('/logout', function (req, res) {
req.session.reset();
});
立即撤销 Persona 会话
与服务器端会话相比,客户端会话的主要缺点之一是服务器不再能够销毁会话。
使用服务器端方案,只需删除存储在服务器上的会话数据就足够了,因为客户端上保留的任何 cookie 现在都将指向不存在的会话。但是,使用客户端方案,会话数据不在服务器上,因此服务器无法确定它是否已在每个客户端上删除。换句话说,我们无法轻松地将服务器状态(用户已注销)与存储在客户端上的状态(用户已登录)同步。
为了弥补此限制,client-sessions 会向 cookie 添加过期时间。在解包存储在加密 cookie 中的会话数据之前,服务器会检查它是否已过期。如果已过期,它将简单地拒绝使用它并将用户视为已注销。
虽然过期时间方案在大多数应用程序中都能正常工作(尤其是在将其设置为相对较低的值时),但在 Persona 的情况下,我们需要一种方法让用户在了解其密码已被泄露后立即撤销其会话。
这意味着需要在后端保留少量状态。我们 使这种即时撤销成为可能 的方法是在用户表以及会话 cookie 中添加一个新令牌。
现在,每个查看 cookie 的 API 调用也会从数据库中读取当前令牌值,并将其与 cookie 中的令牌进行比较。除非它们相同,否则将返回错误,并且用户将注销。
当然,此解决方案的缺点是每次 API 调用都需要额外读取数据库,但幸运的是,我们已经在大多数这些调用中读取了用户表,因此可以同时提取新令牌。
了解更多信息
如果您想试用 client-sessions,请查看此 简单的演示应用程序。然后,如果您发现任何错误,请通过 我们的错误跟踪器 告知我们。
系列中的先前文章
这是 关于 Node.js 的共 12 篇文章系列 的第三部分。前面的文章是
关于 Francois Marier
安全与隐私工程师
关于 Robert Nyman [荣誉编辑]
技术布道师和 Mozilla Hacks 编辑。发表有关 HTML5、JavaScript 和开放 Web 的演讲和博客。Robert 是 HTML5 和开放 Web 的坚定支持者,自 1999 年以来一直从事 Web 前端开发工作 - 在瑞典和纽约市。他还定期在 http://robertnyman.com 上发表博客,并且喜欢旅行和结识新朋友。
17 条评论