注意
此页面生成自 session README。express-session
安装
这是一个通过 npm registry 提供的 Node.js 模块。安装通过 npm install
命令完成
$ npm install express-session
API
var session = require('express-session')
session(options)
使用给定的 options
创建一个会话中间件。
注意 会话数据不存储在 cookie 本身中,只存储会话 ID。会话数据存储在服务器端。
注意 自 1.5.0 版本起,此模块不再需要使用 cookie-parser
中间件即可工作。此模块现在直接在 req
/res
上读取和写入 cookie。如果此模块与 cookie-parser
之间的 secret
不相同,使用 cookie-parser
可能会导致问题。
警告 默认的服务器端会话存储 MemoryStore
并非为生产环境设计。在大多数情况下,它会内存泄漏,无法扩展到单个进程之外,仅用于调试和开发。
有关存储列表,请参阅兼容的会话存储。
选项
express-session
在 options 对象中接受以下属性。
cookie
会话 ID cookie 的设置对象。默认值为 { path: '/', httpOnly: true, secure: false, maxAge: null }
。
以下是可以在此对象中设置的选项。
cookie.domain
指定 Domain
Set-Cookie
属性的值。默认情况下,不设置域,大多数客户端会认为 cookie 仅适用于当前域。
cookie.expires
指定 Date
对象作为 Expires
Set-Cookie
属性的值。默认情况下,不设置过期时间,大多数客户端会将其视为“非持久化 cookie”,并在退出 Web 浏览器应用程序等条件下删除它。
注意 如果 expires
和 maxAge
都已在选项中设置,则使用对象中最后定义的一个。
注意 expires
选项不应直接设置;而应仅使用 maxAge
选项。
cookie.httpOnly
指定 HttpOnly
Set-Cookie
属性的 boolean
值。当为真时,设置 HttpOnly
属性,否则不设置。默认情况下,设置 HttpOnly
属性。
注意 设置为 true
时请小心,因为遵循规范的客户端将不允许客户端 JavaScript 在 document.cookie
中查看此 cookie。
cookie.maxAge
指定用于计算 Expires
Set-Cookie
属性的 number
值(以毫秒为单位)。这是通过获取当前服务器时间并向其添加 maxAge
毫秒来计算 Expires
日期时间。默认情况下,不设置最大年龄。
注意 如果 expires
和 maxAge
都已在选项中设置,则使用对象中最后定义的一个。
cookie.partitioned
指定 Partitioned
Set-Cookie
属性的 boolean
值。当为真时,设置 Partitioned
属性,否则不设置。默认情况下,不设置 Partitioned
属性。
注意 这是一个尚未完全标准化的属性,未来可能会发生变化。这也意味着许多客户端可能会忽略此属性,直到它们理解它。
有关更多信息,请参阅提案。
cookie.path
指定 Path
Set-Cookie
的值。默认情况下,这设置为 '/'
,这是域的根路径。
cookie.priority
指定 Priority
Set-Cookie
属性的 string
值。
'low'
会将Priority
属性设置为Low
。'medium'
会将Priority
属性设置为Medium
,这是未设置时的默认优先级。'high'
会将Priority
属性设置为High
。
有关不同优先级级别的更多信息,请参阅规范。
注意 这是一个尚未完全标准化的属性,未来可能会发生变化。这也意味着许多客户端可能会忽略此属性,直到它们理解它。
cookie.sameSite
指定 boolean
或 string
值作为 SameSite
Set-Cookie
属性的值。默认情况下,此值为 false
。
true
会将SameSite
属性设置为Strict
,用于严格的同站策略强制执行。false
不会设置SameSite
属性。'lax'
会将SameSite
属性设置为Lax
,用于宽松的同站策略强制执行。'none'
会将SameSite
属性设置为None
,用于明确的跨站 cookie。'strict'
会将SameSite
属性设置为Strict
,用于严格的同站策略强制执行。
有关不同强制级别的更多信息,请参阅规范。
注意 这是一个尚未完全标准化的属性,未来可能会发生变化。这也意味着许多客户端可能会忽略此属性,直到它们理解它。
注意 有一个草案规范要求当 SameSite
属性设置为 'none'
时,Secure
属性必须设置为 true
。一些 Web 浏览器或其他客户端可能正在采用此规范。
cookie.secure
指定 Secure
Set-Cookie
属性的 boolean
值。当为真时,设置 Secure
属性,否则不设置。默认情况下,不设置 Secure
属性。
注意 设置为 true
时请小心,因为如果浏览器没有 HTTPS 连接,遵循规范的客户端将来不会将 cookie 发送回服务器。
请注意,secure: true
是一个推荐选项。但是,它需要一个启用 HTTPS 的网站,即 HTTPS 对于安全 cookie 是必需的。如果设置了 secure
,并且您通过 HTTP 访问您的站点,则不会设置 cookie。如果您的 node.js 在代理后面并使用 secure: true
,则需要在 express 中设置“trust proxy”
var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
为了在生产环境中使用安全 cookie,但允许在开发中进行测试,以下是基于 express 中的 NODE_ENV
启用此设置的示例
var app = express()
var sess = {
secret: 'keyboard cat',
cookie: {}
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1) // trust first proxy
sess.cookie.secure = true // serve secure cookies
}
app.use(session(sess))
cookie.secure
选项也可以设置为特殊值 'auto'
,以使此设置自动匹配确定的连接安全性。如果站点同时支持 HTTP 和 HTTPS,请小心使用此设置,因为一旦 cookie 在 HTTPS 上设置,它将不再通过 HTTP 可见。当 Express "trust proxy"
设置正确配置时,这有助于简化开发与生产配置。
genid
用于生成新会话 ID 的函数。提供一个返回字符串的函数,该字符串将用作会话 ID。如果您希望在生成 ID 时使用附加到 req
的某个值,则将 req
作为第一个参数传递给该函数。
默认值是一个使用 uid-safe
库生成 ID 的函数。
注意 请务必生成唯一的 ID,以避免会话冲突。
app.use(session({
genid: function(req) {
return genuuid() // use UUIDs for session IDs
},
secret: 'keyboard cat'
}))
name
要在响应中设置(并从请求中读取)的会话 ID cookie 的名称。
默认值为 'connect.sid'
。
注意 如果您在同一个主机名上运行多个应用程序(这只是名称,例如 localhost
或 127.0.0.1
;不同的方案和端口不会命名不同的主机名),那么您需要将每个应用程序的会话 cookie 分开。最简单的方法是为每个应用程序设置不同的 name
。
proxy
在设置安全 cookie 时(通过“X-Forwarded-Proto”头)信任反向代理。
默认值为 undefined
。
true
将使用“X-Forwarded-Proto”头。false
所有头都将被忽略,并且仅当存在直接 TLS/SSL 连接时,连接才被视为安全。undefined
使用 express 中的“trust proxy”设置
resave
强制将会话保存回会话存储,即使会话在请求期间从未被修改过。根据您的存储,这可能是必要的,但它也可能创建竞态条件,即客户端向您的服务器发出两个并行请求,并且在一个请求中对会话所做的更改可能会在另一个请求结束时被覆盖,即使它没有进行任何更改(此行为也取决于您使用的存储)。
默认值为 true
,但使用默认值已被废弃,因为默认值将来会改变。请研究此设置并选择适合您用例的值。通常,您会希望使用 false
。
如何判断我的存储是否需要此设置?最好的方法是检查您的存储是否实现了 touch
方法。如果实现了,那么您可以安全地设置 resave: false
。如果未实现 touch
方法,并且您的存储对存储的会话设置了过期日期,那么您可能需要 resave: true
。
rolling
强制在每个响应上设置会话标识符 cookie。过期时间会重置为原始的 maxAge
,从而重置过期倒计时。
默认值为 false
。
启用此选项后,会话标识符 cookie 将在上次响应发送后的 maxAge
时间内过期,而不是在服务器上次修改会话后的 maxAge
时间内过期。
这通常与较短的、非会话长度的 maxAge
值结合使用,以提供会话数据的快速超时,同时减少在正在进行的服务器交互期间发生超时的可能性。
注意 当此选项设置为 true
但 saveUninitialized
选项设置为 false
时,对于未初始化的会话,响应中不会设置 cookie。此选项仅在为请求加载了现有会话时修改行为。
saveUninitialized
强制将“未初始化”的会话保存到存储中。当会话是新的但未被修改时,它被认为是未初始化的。选择 false
对于实现登录会话、减少服务器存储使用或遵守在设置 cookie 前需要许可的法律很有用。选择 false
还有助于解决客户端在没有会话的情况下发出多个并行请求时出现的竞态条件。
默认值为 true
,但使用默认值已被废弃,因为默认值将来会改变。请研究此设置并选择适合您用例的值。
注意 如果您将 Session 与 PassportJS 结合使用,Passport 将向会话添加一个空的 Passport 对象,以便在用户通过身份验证后使用,这将被视为对会话的修改,从而导致会话被保存。此问题已在 PassportJS 0.3.0 中修复
secret
必需选项
这是用于签署会话 ID cookie 的密钥。该密钥可以是 Node.js crypto.createHmac
支持的任何类型的值(例如字符串或 Buffer
)。这可以是一个单一密钥,也可以是多个密钥的数组。如果提供密钥数组,则只有第一个元素将用于签署会话 ID cookie,而所有元素都将在请求中验证签名时考虑。密钥本身不应容易被人解析,最好是一组随机字符。最佳实践可能包括
- 使用环境变量存储密钥,确保密钥本身不存在于您的代码仓库中。
- 定期更新密钥,同时确保前一个密钥在数组中。
使用无法猜测的密钥将把劫持会话的能力降低到仅能猜测会话 ID(由 genid
选项确定)。
更改密钥值将使所有现有会话失效。为了在不使会话失效的情况下轮换密钥,请提供一个密钥数组,其中新密钥作为数组的第一个元素,并将之前的密钥作为后面的元素。
注意 HMAC-256 用于签署会话 ID。因此,密钥应至少包含 32 字节的熵。
store
会话存储实例,默认为一个新的 MemoryStore
实例。
unset
控制取消设置 req.session
的结果(通过 delete
、设置为 null
等)。
默认值为 'keep'
。
'destroy'
响应结束时,会话将被销毁(删除)。'keep'
存储中的会话将被保留,但请求期间所做的修改将被忽略且不保存。
req.session
要存储或访问会话数据,只需使用请求属性 req.session
,它(通常)由存储序列化为 JSON,因此嵌套对象通常没问题。例如,下面是一个用户特定的访问计数器
// Use the session middleware
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
// Access the session as req.session
app.get('/', function(req, res, next) {
if (req.session.views) {
req.session.views++
res.setHeader('Content-Type', 'text/html')
res.write('<p>views: ' + req.session.views + '</p>')
res.write('<p>expires in: ' + (req.session.cookie.maxAge / 1000) + 's</p>')
res.end()
} else {
req.session.views = 1
res.end('welcome to the session demo. refresh!')
}
})
Session.regenerate(callback)
只需调用此方法即可重新生成会话。完成后,一个新的 SID 和 Session
实例将在 req.session
处初始化,并且 callback
将被调用。
req.session.regenerate(function(err) {
// will have a new session here
})
Session.destroy(callback)
销毁会话并取消设置 req.session
属性。完成后,将调用 callback
。
req.session.destroy(function(err) {
// cannot access session here
})
Session.reload(callback)
从存储重新加载会话数据并重新填充 req.session
对象。完成后,将调用 callback
。
req.session.reload(function(err) {
// session updated
})
Session.save(callback)
将会话保存回存储,用内存中的内容替换存储上的内容(尽管存储可能会做其他事情——请查阅存储的文档以了解确切行为)。
如果会话数据已被更改(尽管此行为可以通过中间件构造函数中的各种选项进行更改),此方法将在 HTTP 响应结束时自动调用。因此,通常不需要调用此方法。
在某些情况下调用此方法很有用,例如重定向、长时间请求或在 WebSockets 中。
req.session.save(function(err) {
// session saved
})
Session.touch()
更新 .maxAge
属性。通常不需要调用此方法,因为会话中间件会为您完成此操作。
req.session.id
每个会话都有一个与其关联的唯一 ID。此属性是 req.sessionID
的别名,不能修改。添加它是为了使会话 ID 可以从 session
对象访问。
req.session.cookie
每个会话都伴随着一个唯一的 cookie 对象。这允许您根据每个访问者更改会话 cookie。例如,我们可以将 req.session.cookie.expires
设置为 false
,以使 cookie 仅在用户代理的持续时间内保留。
Cookie.maxAge
或者,req.session.cookie.maxAge
将返回剩余时间(以毫秒为单位),我们也可以重新分配一个新值以适当调整 .expires
属性。以下内容本质上是等效的
var hour = 3600000
req.session.cookie.expires = new Date(Date.now() + hour)
req.session.cookie.maxAge = hour
例如,当 maxAge
设置为 60000
(一分钟),并且已过去 30 秒时,它将返回 30000
,直到当前请求完成,此时调用 req.session.touch()
将 req.session.cookie.maxAge
重置为其原始值。
req.session.cookie.maxAge // => 30000
Cookie.originalMaxAge
req.session.cookie.originalMaxAge
属性返回会话 cookie 的原始 maxAge
(生存时间),以毫秒为单位。
req.sessionID
要获取已加载会话的 ID,请访问请求属性 req.sessionID
。这只是在加载/创建会话时设置的只读值。
会话存储实现
每个会话存储必须是一个 EventEmitter
并实现特定方法。以下是必需、推荐和可选方法的列表。
- 必需方法是此模块将始终在存储上调用的方法。
- 推荐方法是此模块在可用时将在存储上调用的方法。
- 可选方法是此模块完全不调用的方法,但有助于向用户呈现统一的存储。
有关示例实现,请查看 connect-redis 仓库。
store.all(callback)
可选
此可选方法用于将存储中的所有会话作为数组获取。callback
应以 callback(error, sessions)
的形式调用。
store.destroy(sid, callback)
必需
此必需方法用于根据会话 ID (sid
) 从存储中销毁/删除会话。会话销毁后,应以 callback(error)
的形式调用 callback
。
store.clear(callback)
可选
此可选方法用于从存储中删除所有会话。存储清除后,应以 callback(error)
的形式调用 callback
。
store.length(callback)
可选
此可选方法用于获取存储中所有会话的数量。callback
应以 callback(error, len)
的形式调用。
store.get(sid, callback)
必需
此必需方法用于根据会话 ID (sid
) 从存储中获取会话。callback
应以 callback(error, session)
的形式调用。
如果找到会话,session
参数应为会话,否则如果未找到会话(且没有错误),则为 null
或 undefined
。当 error.code === 'ENOENT'
时,会做特殊处理,使其行为类似于 callback(null, null)
。
store.set(sid, session, callback)
必需
此必需方法用于根据会话 ID (sid
) 和会话 (session
) 对象将会话插入或更新到存储中。一旦会话在存储中设置完毕,应以 callback(error)
的形式调用 callback。
store.touch(sid, session, callback)
推荐
此推荐方法用于根据会话 ID (sid
) 和会话 (session
) 对象“触摸”给定的会话。会话被触摸后,应以 callback(error)
的形式调用 callback
。
这主要用于当存储会自动删除空闲会话时,此方法用于向存储发出信号,表示给定会话处于活动状态,可能会重置空闲计时器。
兼容的会话存储
以下模块实现了与此模块兼容的会话存储。请提交 PR 以添加更多模块 :)
aerospike-session-store 使用 Aerospike 的会话存储。
better-sqlite3-session-store 基于 better-sqlite3 的会话存储。
cassandra-store 一个基于 Apache Cassandra 的会话存储。
cluster-store 一个用于在 Node 集群中使用进程内/嵌入式存储(例如 SQLite (通过 knex)、leveldb、文件或内存)的包装器(适用于树莓派 2 和其他多核嵌入式设备)。
connect-arango 一个基于 ArangoDB 的会话存储。
connect-azuretables 一个基于 Azure Table Storage 的会话存储。
connect-cloudant-store 一个基于 IBM Cloudant 的会话存储。
connect-cosmosdb 一个基于 Azure Cosmos DB 的会话存储。
connect-couchbase 一个基于 couchbase 的会话存储。
connect-datacache 一个基于 IBM Bluemix Data Cache 的会话存储。
@google-cloud/connect-datastore 一个基于 Google Cloud Datastore 的会话存储。
connect-db2 一个使用 ibm_db 模块构建的 IBM DB2 会话存储。
connect-dynamodb 一个基于 DynamoDB 的会话存储。
@google-cloud/connect-firestore 一个基于 Google Cloud Firestore 的会话存储。
connect-hazelcast 适用于 Connect 和 Express 的 Hazelcast 会话存储。
connect-loki 一个基于 Loki.js 的会话存储。
connect-lowdb 一个基于 lowdb 的会话存储。
connect-memcached 一个基于 memcached 的会话存储。
connect-memjs 一个使用 memjs 作为 memcached 客户端的 memcached 会话存储。
connect-ml 一个基于 MarkLogic Server 的会话存储。
connect-monetdb 一个基于 MonetDB 的会话存储。
connect-mongo 一个基于 MongoDB 的会话存储。
connect-mongodb-session 由 MongoDB 构建和维护的轻量级 MongoDB 会话存储。
connect-mssql-v2 一个基于 connect-mssql 的 Microsoft SQL Server 会话存储。
connect-neo4j 一个基于 Neo4j 的会话存储。
connect-ottoman 一个基于 couchbase ottoman 的会话存储。
connect-pg-simple 一个基于 PostgreSQL 的会话存储。
connect-redis 一个基于 Redis 的会话存储。
connect-session-firebase 一个基于 Firebase Realtime Database 的会话存储。
connect-session-knex 一个使用 Knex.js 的会话存储,Knex.js 是一个用于 PostgreSQL、MySQL、MariaDB、SQLite3 和 Oracle 的 SQL 查询构建器。
connect-session-sequelize 一个使用 Sequelize.js 的会话存储,Sequelize.js 是一个用于 PostgreSQL、MySQL、SQLite 和 MSSQL 的 Node.js / io.js ORM。
connect-sqlite3 一个基于 SQLite3 的会话存储,仿照 TJ 的
connect-redis
存储。
connect-typeorm 一个基于 TypeORM 的会话存储。
couchdb-expression 一个基于 CouchDB 的会话存储。
dynamodb-store 一个基于 DynamoDB 的会话存储。
dynamodb-store-v3 一个使用 DynamoDB 并由 AWS SDK for JavaScript v3 支持的会话存储实现。
express-etcd 一个基于 etcd 的会话存储。
express-mysql-session 一个通过 node-mysql 模块使用原生 MySQL 的会话存储。
express-nedb-session 一个基于 NeDB 的会话存储。
express-oracle-session 一个通过 node-oracledb 模块使用原生 Oracle 的会话存储。
express-session-cache-manager 一个实现了 cache-manager 的存储,它支持多种存储类型。
express-session-etcd3 一个基于 etcd3 的会话存储。
express-session-level 一个基于 LevelDB 的会话存储。
express-session-rsdb 基于 Rocket-Store 的会话存储:一个非常简单、超快且功能强大的扁平文件数据库。
express-sessions 一个同时支持 MongoDB 和 Redis 的会话存储。
firestore-store 一个基于 Firestore 的会话存储。
fortune-session 一个基于 Fortune.js 的会话存储。支持 Fortune 支持的所有后端(MongoDB、Redis、Postgres、NeDB)。
hazelcast-store 一个基于 Hazelcast Node Client 构建的 Hazelcast 会话存储。
level-session-store 一个基于 LevelDB 的会话存储。
lowdb-session-store 一个基于 lowdb 的会话存储。
medea-session-store 一个基于 Medea 的会话存储。
memorystore 一个为生产环境设计的内存会话存储。
mssql-session-store 一个基于 SQL Server 的会话存储。
nedb-session-store 一个替代的基于 NeDB(内存或文件持久化)的会话存储。
@quixo3/prisma-session-store 一个用于 Prisma Framework 的会话存储。
restsession 利用 RESTful API 存储会话。
sequelstore-connect 一个使用 Sequelize.js 的会话存储。
session-file-store 一个基于文件系统的会话存储。
session-pouchdb-store 适用于 PouchDB / CouchDB 的会话存储。支持嵌入式、自定义或远程 PouchDB 实例以及实时同步。
@cyclic.sh/session-store 一个用于 Cyclic.sh 应用程序的 DynamoDB 会话存储。
@databunker/session-store 一个基于 Databunker 的加密会话存储。
sessionstore 一个适用于各种数据库的会话存储。
tch-nedb-session 一个基于 NeDB 的文件系统会话存储。
示例
访问计数器
一个使用 express-session
存储用户页面访问量的简单示例。
var express = require('express')
var parseurl = require('parseurl')
var session = require('express-session')
var app = express()
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
app.use(function (req, res, next) {
if (!req.session.views) {
req.session.views = {}
}
// get the url pathname
var pathname = parseurl(req).pathname
// count the views
req.session.views[pathname] = (req.session.views[pathname] || 0) + 1
next()
})
app.get('/foo', function (req, res, next) {
res.send('you viewed this page ' + req.session.views['/foo'] + ' times')
})
app.get('/bar', function (req, res, next) {
res.send('you viewed this page ' + req.session.views['/bar'] + ' times')
})
app.listen(3000)
用户登录
一个使用 express-session
保持用户登录会话的简单示例。
var escapeHtml = require('escape-html')
var express = require('express')
var session = require('express-session')
var app = express()
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
// middleware to test if authenticated
function isAuthenticated (req, res, next) {
if (req.session.user) next()
else next('route')
}
app.get('/', isAuthenticated, function (req, res) {
// this is only called when there is an authentication user due to isAuthenticated
res.send('hello, ' + escapeHtml(req.session.user) + '!' +
' <a href="/logout">Logout</a>')
})
app.get('/', function (req, res) {
res.send('<form action="/login" method="post">' +
'Username: <input name="user"><br>' +
'Password: <input name="pass" type="password"><br>' +
'<input type="submit" text="Login"></form>')
})
app.post('/login', express.urlencoded({ extended: false }), function (req, res) {
// login logic to validate req.body.user and req.body.pass
// would be implemented here. for this example any combo works
// regenerate the session, which is good practice to help
// guard against forms of session fixation
req.session.regenerate(function (err) {
if (err) next(err)
// store user information in session, typically a user id
req.session.user = req.body.user
// save the session before redirection to ensure page
// load does not happen before session is saved
req.session.save(function (err) {
if (err) return next(err)
res.redirect('/')
})
})
})
app.get('/logout', function (req, res, next) {
// logout logic
// clear the user from the session object and save.
// this will ensure that re-using the old session id
// does not have a logged in user
req.session.user = null
req.session.save(function (err) {
if (err) next(err)
// regenerate the session, which is good practice to help
// guard against forms of session fixation
req.session.regenerate(function (err) {
if (err) next(err)
res.redirect('/')
})
})
})
app.listen(3000)
调试
此模块内部使用 debug 模块来记录会话操作的信息。
要查看所有内部日志,请在启动应用程序时(例如,npm start
)将 DEBUG
环境变量设置为 express-session
$ DEBUG=express-session npm start
在 Windows 上,使用相应的命令;
> set DEBUG=express-session & npm start