在我们的语境中,**会话**被定义为几个组件的集合,它们共同允许您在请求之间存储有关用户代理的信息。
**用户代理**是指代表您在设备上运行的软件(浏览器或原生应用程序)(例如,您计算机上的浏览器标签、智能手机应用程序或您的冰箱)。它与 cookie 或访问令牌一一对应。
会话非常有用,因为请求/响应周期是**无状态**的。请求/响应周期被认为是无状态的,因为客户端和服务器都没有固有地在不同请求之间存储有关特定请求的任何信息。因此,请求/响应的生命周期在向请求的用户代理发送响应时结束(例如,res.send()
)。
注意:我们将讨论浏览器用户代理环境中的会话。虽然您可以在 Sails 中将会话用于任何您喜欢的事情,但通常最好将它们纯粹用于存储用户代理身份验证的状态。身份验证是一个允许用户代理证明其拥有特定身份的过程。例如,为了访问某些受保护的功能,我可能需要证明我的浏览器标签实际上对应于数据库中的特定用户记录。如果您向我提供一个唯一的名称和密码,您可以查找该名称并将我的密码与存储的(希望是加密的)密码进行比较。如果匹配,则我已通过身份验证。但是,您如何在请求之间存储这种“已验证”的状态?这就是会话发挥作用的地方。
Sails 中的会话实现包含三个主要组件
sails.sid
)**会话存储**可以位于内存中(这是默认的 Sails 会话存储)或数据库中(Sails 内置支持使用 Redis 来实现此目的)。Sails 基于 Connect 中间件来管理会话,其中包括使用**cookie**在用户代理上存储会话 ID(sid
)。
当向 Sails 发送请求时,会话中间件会解析请求标头。
如果标头不包含 cookie,将在会话中创建 sid
,并将默认会话字典添加到 req
(例如,req.session
)。此时,您可以对会话属性进行更改(通常在控制器/操作中)。例如,让我们看一下以下登录操作
module.exports = {
login: function(req, res) {
// Authentication code here
// If successfully authenticated
req.session.userId = foundUser.id; // returned from a database
return res.json(foundUser);
}
}
这里,我们在 req.session
中添加了 userId
属性。
**注意:**该属性不会存储在会话存储中,也不会在响应发送之前对其他请求可用。
响应发送后,任何新的请求都可以访问 req.session.userId
。由于我们在请求标头中没有 cookie,因此将为我们建立一个 cookie。
Sails.sid
的 cookie现在,当用户代理发出下一个请求时,将检查存储在 cookie 上的 Sails.sid
的真实性。如果它与会话存储中现有的 sid
匹配,则会话存储的内容将作为属性添加到 req
字典(req.session
)中。我们可以访问 req.session
上的属性(例如 req.session.userId
)或设置其属性(例如 req.session.userId == someValue
)。会话存储中的值可能会发生变化,但 Sails.sid
和 sid
通常不会发生变化。
Sails.sid
什么时候会改变?在开发过程中,Sails 会话存储位于内存中。因此,当您关闭 Sails 服务器时,当前会话存储会消失。当 Sails 重新启动时,即使用户代理请求在 cookie 中包含 Sails.sid
,但 sid
不再位于会话存储中。因此,将生成一个新的 sid
并在 cookie 中替换它。如果用户代理 cookie 过期或被删除,Sails.sid
也会发生变化。
可以通过访问
projectName/config/session.js
中的cookie.maxAge
属性,将 Sails cookie 的生存期从其默认设置(永不过期)更改为新的设置。
Redis 是一个键值数据库包,可以用作与 Sails 实例分离的会话存储。这种会话配置有两个好处。第一,会话存储将在 Sails 重启之间保持有效。第二,如果您在负载均衡器后面有多个 Sails 实例,则所有实例都可以指向单个合并的会话存储。
要在开发中启用 Redis 作为您的会话存储,首先确保您在机器上运行了一个本地的 Redis 实例(redis-server
)。然后,使用 sails lift --redis
启动您的应用程序。
这只是 sails lift --session.adapter=@sailshq/connect-redis --sockets.adapter=@sailshq/socket.io-redis
的快捷方式。这些包默认情况下包含在新 Sails 应用程序的依赖项中,但如果您正在使用升级的应用程序,则需要 npm install @sailshq/connect-redis
和 npm install @sailshq/socket.io-redis
。
注意,此内置配置使用您的本地 Redis 实例。有关高级会话配置选项,请参阅参考 > 配置 > sails.config.session。
cookie 的值是通过首先使用可配置的secret(只是一个长字符串)对 sid
进行哈希运算创建的。
您可以在
projectName/config/session.js
中更改会话secret
属性。
然后,Sails sid
(例如,Sails.sid
)将成为普通 sid
与 sid
加上 secret
的哈希值的组合。为了脱离抽象世界,让我们举个例子。Sails 创建一个 sid
为 234lj232hg234jluy32UUYUHH
,session secret
为 9238cca11a83d473e10981c49c4f
。这些值只是 Sails 组合和哈希以创建 signature
为 AuSosBAbL9t3Ev44EofZtIpiMuV7fB2oi
的两个字符串。因此,Sails.sid
成为 234lj232hg234jluy32UUYUHH.AuSosBAbL9t3Ev44EofZtIpiMuV7fB2oi
并通过在响应标头中发送 set-cookie
属性存储在用户代理 cookie 中。
**这可以防止什么?**这可以防止用户猜测 sid
。它还可以防止恶意攻击者欺骗用户使用恶意攻击者知道的 sid
发出身份验证请求。这可能会允许恶意攻击者使用 sid
在用户通过会话进行身份验证时做一些不好的事情。
即使您的 Sails 应用程序旨在被非浏览器客户端(例如烤面包机)访问,也强烈建议您使用会话进行身份验证。虽然有时可能难以理解,但 Sails 中的内置会话机制(会话存储 + HTTP 仅 cookie)是一个经过验证且可靠的解决方案,通常比您自己推出其他方案更不脆弱、更易于使用且风险更低。
也就是说,会话可能并非总是可行(例如,如果您必须与其他身份验证方案集成,如 JWT)。在这些情况下,您可以禁用整个应用程序或每个请求的会话。
要完全关闭应用程序的会话支持,请将以下内容添加到您的 .sailsrc
文件中
"hooks": {
"session": false
}
这将禁用核心 Sails 会话钩子。您也可以通过将 sails_hooks__session
环境变量设置为 false
来实现这一点。
要在每个路由(或每个请求)的基础上关闭会话支持,请使用sails.config.session.isSessionDisabled
设置。默认情况下,Sails 会为所有请求启用会话支持,除了那些看起来像指向图像、样式表等静态资产的请求。