这是一个 Node.js 模块,可通过 npm 注册表 获得。安装使用 npm install
命令 完成
$ npm install express-session
var session = require('express-session')
使用给定的 options
创建一个会话中间件。
注意 会话数据不会保存在 cookie 本身中,只保存会话 ID。会话数据存储在服务器端。
注意 自 1.5.0 版本起,不再需要使用 cookie-parser
中间件 来使此模块工作。此模块现在直接在 req
/res
上读取和写入 cookie。如果 secret
在此模块和 cookie-parser
之间不相同,使用 cookie-parser
可能会导致问题。
警告 默认的服务器端会话存储,MemoryStore
,故意不设计用于生产环境。在大多数情况下它会泄露内存,无法扩展到单个进程之外,并且仅用于调试和开发。
有关存储列表,请参见 兼容的会话存储。
express-session
接受选项对象中的这些属性。
会话 ID cookie 的设置对象。默认值为 { path: '/', httpOnly: true, secure: false, maxAge: null }
。
以下是在此对象中可以设置的选项。
指定 Domain
Set-Cookie
属性的值。默认情况下,不设置域,并且大多数客户端会认为 cookie 仅适用于当前域。
指定 Date
对象作为 Expires
Set-Cookie
属性的值。默认情况下,不设置过期时间,并且大多数客户端会将其视为“非持久性 cookie”,并在退出 Web 浏览器应用程序等条件下将其删除。
注意如果在选项中同时设置了 expires
和 maxAge
,则使用对象中定义的最后一个选项。
注意不应直接设置 expires
选项;而应仅使用 maxAge
选项。
指定 HttpOnly
Set-Cookie
属性的 boolean
值。当为真时,设置 HttpOnly
属性,否则不设置。默认情况下,设置 HttpOnly
属性。
注意在将此项设置为 true
时要小心,因为符合条件的客户端不允许客户端 JavaScript 在 document.cookie
中查看 cookie。
指定在计算 Expires
Set-Cookie
属性时要使用的 number
(以毫秒为单位)。这是通过获取当前服务器时间并将 maxAge
毫秒添加到该值来计算 Expires
日期时间来完成的。默认情况下,不设置最大年龄。
注意如果在选项中同时设置了 expires
和 maxAge
,则使用对象中定义的最后一个选项。
指定 Path
Set-Cookie
的值。默认情况下,将其设置为 '/'
,这是域的根路径。
指定 boolean
或 string
作为 SameSite
Set-Cookie
属性的值。默认情况下,此值为 false
。
true
将 SameSite
属性设置为 Strict
以严格执行同站策略。false
将不设置 SameSite
属性。'lax'
将为宽松同站强制设置 SameSite
属性为 Lax
。'none'
将为显式跨站 cookie 设置 SameSite
属性为 None
。'strict'
将为严格同站强制设置 SameSite
属性为 Strict
。有关不同强制级别的更多信息,请参阅 规范。
注意 这是一个尚未完全标准化的属性,将来可能会发生更改。这也意味着许多客户端可能在理解此属性之前会忽略它。
注意 有一个 草案规范 要求在 SameSite
属性已设置为 'none'
时将 Secure
属性设置为 true
。一些网络浏览器或其他客户端可能会采用此规范。
指定 Secure
Set-Cookie
属性的 boolean
值。当为真时,设置 Secure
属性,否则不设置。默认情况下,不设置 Secure
属性。
注意 在将此设置为 true
时要小心,因为如果浏览器没有 HTTPS 连接,则符合要求的客户端将来不会将 cookie 发送回服务器。
请注意,secure: true
是一个推荐选项。但是,它需要一个启用 https 的网站,即 HTTPS 对于安全 cookie 是必需的。如果设置了 secure
,并且您通过 HTTP 访问您的网站,则不会设置 cookie。如果您将 node.js 放在代理后面并且使用 secure: true
,则需要在 express 中设置“信任代理”
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 提供,在使用此设置时要小心,因为一旦在 HTTPS 上设置 cookie,它将不再通过 HTTP 可见。当 Express "trust proxy"
设置正确设置以简化开发与生产配置时,这很有用。
要调用的函数以生成新的会话 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'
}))
在响应中设置会话 ID Cookie 的名称(并在请求中读取)。
默认值为 'connect.sid'
。
注意如果在同一主机名上运行多个应用(这只是名称,例如 localhost
或 127.0.0.1
;不同的方案和端口不会命名不同的主机名),则需要将会话 Cookie 相互分离。最简单的方法是为每个应用设置不同的 name
。
在设置安全 Cookie 时信任反向代理(通过“X-Forwarded-Proto”标头)。
默认值为 undefined
。
true
将使用“X-Forwarded-Proto”标头。false
忽略所有标头,并且仅在存在直接 TLS/SSL 连接时才将连接视为安全。undefined
使用 express 中的“信任代理”设置强制将会话保存回会话存储,即使会话在请求期间从未修改过。根据您的存储,这可能是有必要的,但它也可能创建竞争条件,其中客户端向您的服务器发出两个并行请求,并且在一个请求中对会话所做的更改可能会在另一个请求结束时被覆盖,即使它没有进行任何更改(此行为还取决于您使用的存储)。
默认值为 true
,但使用默认值已被弃用,因为默认值将来会更改。请研究此设置并选择适合您用例的内容。通常,您需要 false
。
我如何知道这对我的商店是否必要?了解的最佳方法是检查您的商店是否实现了 touch
方法。如果是,则可以安全地设置 resave: false
。如果它没有实现 touch
方法并且您的商店在存储的会话上设置了到期日期,那么您可能需要 resave: true
。
强制在每次响应中设置会话标识符 Cookie。到期时间重置为原始 maxAge
,重置到期倒计时。
默认值为 false
。
启用此功能后,会话标识符 Cookie 将在自上次发送响应以来 maxAge
中过期,而不是自服务器上次修改会话以来 maxAge
中过期。
这通常与短的、非会话长度的 maxAge
值结合使用,以快速超时会话数据,并减少在进行的服务器交互期间发生这种情况的可能性。
注意 当此选项设置为 true
但 saveUninitialized
选项设置为 false
时,cookie 将不会在具有未初始化会话的响应上设置。此选项仅在为请求加载现有会话时修改行为。
强制将“未初始化”的会话保存到存储中。会话在新建但未修改时处于未初始化状态。选择 false
对于实现登录会话、减少服务器存储使用或遵守在设置 cookie 之前需要获得许可的法律非常有用。选择 false
还可以帮助解决客户端在没有会话的情况下发出多个并行请求时的竞争条件。
默认值为 true
,但使用默认值已被弃用,因为默认值将在将来更改。请研究此设置并选择适合您用例的内容。
注意 如果您将 Session 与 PassportJS 结合使用,Passport 将向会话中添加一个空的 Passport 对象,以便在用户经过身份验证后使用,这将被视为对会话的修改,从而导致其被保存。此问题已在 PassportJS 0.3.0 中得到修复
必需选项
这是用于签名会话 ID cookie 的密钥。它可以是单个密钥的字符串,也可以是多个密钥的数组。如果提供了密钥数组,则仅使用第一个元素来签名会话 ID cookie,而在请求中验证签名时将考虑所有元素。密钥本身不应该容易被人解析,最好是一组随机字符。最佳实践可能包括
使用无法猜测的密钥将劫持会话的能力仅降低为猜测会话 ID(由 genid
选项确定)。
更改密钥值将使所有现有会话无效。为了在不使会话无效的情况下轮换密钥,请提供一个密钥数组,其中新密钥为数组的第一个元素,并将以前的密钥作为后面的元素。
会话存储实例,默认为新的 MemoryStore
实例。
控制取消设置 req.session
(通过 delete
,设置为 null
等)的结果。
默认值为 'keep'
。
'destroy'
当响应结束时,会话将被销毁(删除)。'keep'
存储中的会话将被保留,但在请求期间进行的修改将被忽略且不保存。要存储或访问会话数据,只需使用请求属性 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!')
}
})
要重新生成会话,只需调用该方法。完成后,将在 req.session
处初始化一个新的 SID 和 Session
实例,并将调用 callback
。
req.session.regenerate(function(err) {
// will have a new session here
})
销毁会话并将取消设置 req.session
属性。完成后,将调用 callback
。
req.session.destroy(function(err) {
// cannot access session here
})
从存储中重新加载会话数据并重新填充 req.session
对象。完成后,将调用 callback
。
req.session.reload(function(err) {
// session updated
})
将会话保存回存储,用存储中的内容替换内存中的内容(尽管存储可能执行其他操作——请查阅存储文档了解确切行为)。
如果会话数据已更改,则在 HTTP 响应结束时会自动调用此方法(尽管可以通过中间件构造函数中的各种选项来更改此行为)。因此,通常不需要调用此方法。
在某些情况下,调用此方法很有用,例如重定向、长时间运行的请求或在 WebSockets 中。
req.session.save(function(err) {
// session saved
})
更新 .maxAge
属性。通常不需要调用此方法,因为会话中间件会为您执行此操作。
每个会话都有一个与之关联的唯一 ID。此属性是 req.sessionID
的别名,且不能修改。添加此属性是为了使会话 ID 可从 session
对象访问。
每个会话都有一个唯一的 cookie 对象随附。这允许您为每个访问者更改会话 cookie。例如,我们可以将 req.session.cookie.expires
设置为 false
,以使 cookie 仅在用户代理持续期间内保留。
或者,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
req.session.cookie.originalMaxAge
属性返回会话 cookie 的原始 maxAge
(生存时间),以毫秒为单位。
要获取已加载会话的 ID,请访问请求属性 req.sessionID
。这仅仅是在加载/创建会话时设置的一个只读值。
每个会话存储必须是一个 EventEmitter
并实现特定方法。以下方法是必需、推荐和可选方法的列表。
有关示例实现,请查看 connect-redis 存储库。
可选
此可选方法用于以数组形式获取存储中的所有会话。callback
应作为 callback(error, sessions)
调用。
必需
此必需方法用于根据会话 ID (sid
) 从存储中销毁/删除会话。销毁会话后,应将 callback
作为 callback(error)
调用。
可选
此可选方法用于从存储中删除所有会话。清除存储后,应将 callback
作为 callback(error)
调用。
可选
此可选方法用于获取存储中所有会话的数量。callback
应作为 callback(error, len)
调用。
必需
此必需方法用于根据会话 ID (sid
) 从存储中获取会话。callback
应作为 callback(error, session)
调用。
如果找到会话,session
参数应为会话,否则如果未找到会话(且没有错误),则为 null
或 undefined
。当 error.code === 'ENOENT'
时,会进行特殊处理,以执行类似于 callback(null, null)
的操作。
必需
此必需方法用于根据会话 ID (sid
) 和会话 (session
) 对象将会话插入或更新到存储中。将会话设置到存储中后,应将回调作为 callback(error)
调用。
推荐
此推荐方法用于“触碰”给定的会话,给定会话 ID (sid
) 和会话 (session
) 对象。一旦会话被触碰,应将 callback
作为 callback(error)
调用。
这主要用于存储将自动删除空闲会话,此方法用于向存储发出信号,表明给定会话处于活动状态,可能会重置空闲计时器。
以下模块实现与本模块兼容的会话存储。请提交 PR 以添加其他模块 :)
aerospike-session-store 使用 Aerospike 的会话存储。
better-sqlite3-session-store 基于 better-sqlite3 的会话存储。
cassandra-store 基于 Apache Cassandra 的会话存储。
cluster-store 用于在进程内/嵌入式存储中使用的包装器 - 如 SQLite(通过 knex)、leveldb、文件或内存 - 使用节点集群(适用于 Raspberry Pi 2 和其他多核嵌入式设备)。
connect-arango 基于 ArangoDB 的会话存储。
connect-azuretables 基于 Azure 表存储 的会话存储。
connect-cloudant-store 基于 IBM Cloudant 的会话存储。
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-pg-simple 基于 PostgreSQL 的会话存储。
connect-redis 基于 Redis 的会话存储。
connect-session-firebase 基于 Firebase 实时数据库 的会话存储
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 的会话存储。
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 的会话存储,构建在 Hazelcast Node Client 之上。
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 实例和实时同步。
session-rethinkdb 基于 RethinkDB 的会话存储。
@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