黑人生命也是命。
支持平等正義倡議.

express-session

NPM Version NPM Downloads Build Status Test Coverage

安裝

這是透過 npm 註冊 取得的 Node.js 模組。安裝時使用 npm install 指令

$ npm install express-session

API

var session = require('express-session')

session(options)

使用指定的 options 建立一個 session 中介軟體。

注意 會話資料並不會儲存在 cookie 本身,只會儲存會話 ID。會話資料儲存在伺服器端。

注意 從 1.5.0 版開始,這個模組不再需要使用 cookie-parser 中介軟體 才能運作。這個模組現在會直接在 req/res 上讀取和寫入 cookie。如果這個模組和 cookie-parsersecret 不同,使用 cookie-parser 可能會造成問題。

警告 預設的伺服器端會話儲存,MemoryStore故意沒有設計成用於生產環境。在大多數情況下,它會造成記憶體外洩,無法擴充到單一程序之外,而且是為了除錯和開發而設計的。

如需儲存清單,請參閱 相容的會話儲存

選項

express-session 接受選項物件中的這些屬性。

會話 ID cookie 的設定物件。預設值為 { path: '/', httpOnly: true, secure: false, maxAge: null }

以下是可以在這個物件中設定的選項。

cookie.domain

指定 Domain Set-Cookie 屬性的值。預設情況下,不會設定網域,而且大多數用戶端會認為 cookie 只套用於目前的網域。

cookie.expires

指定 Date 物件為 Expires Set-Cookie 屬性的值。預設情況下,不會設定到期日,而且大多數用戶端會將其視為「非持續性 cookie」,並在退出網頁瀏覽器應用程式等條件下將其刪除。

注意 如果在選項中同時設定 expiresmaxAge,則會使用物件中定義的最後一個選項。

注意 expires 選項不應直接設定;請改用 maxAge 選項。

cookie.httpOnly

指定 HttpOnly Set-Cookie 屬性的 boolean 值。為真時,會設定 HttpOnly 屬性,否則不會設定。預設會設定 HttpOnly 屬性。

注意 將此設定為 true 時請小心,因為相容的用戶端會禁止用戶端 JavaScript 在 document.cookie 中看到 cookie。

cookie.maxAge

計算 Expires Set-Cookie 屬性時,指定要使用的 number(以毫秒為單位)。這會取用目前的伺服器時間,並將 maxAge 毫秒加到該值中,以計算 Expires 日期時間。預設不會設定最大期限。

注意 如果在選項中同時設定 expiresmaxAge,則會使用物件中定義的最後一個選項。

cookie.path

指定 Path Set-Cookie 的值。預設設定為 '/',也就是網域的根路徑。

cookie.sameSite

指定 SameSite Set-Cookie 屬性的值為 booleanstring。預設為 false

  • true 會將 SameSite 屬性設定為 Strict,以強制執行嚴格的同站政策。
  • false 不會設定 SameSite 屬性。
  • 'lax' 會將 SameSite 屬性設定為 Lax,以強制執行寬鬆的同站政策。
  • 'none' 會將 SameSite 屬性設定為 None,以設定明確的跨站 cookie。
  • 'strict' 會將 SameSite 屬性設定為 Strict,以強制執行嚴格的同站政策。

可以在 規範 中找到有關不同強制執行層級的更多資訊。

注意 這是尚未完全標準化的屬性,未來可能會變更。這也表示許多用戶端可能會忽略此屬性,直到他們了解它為止。

注意草案規範 要求當 SameSite 屬性設定為 'none' 時,Secure 屬性必須設定為 true。有些網路瀏覽器或其他用戶端可能會採用此規範。

cookie.secure

指定 Secure Set-Cookie 屬性的 boolean 值。當為真時,會設定 Secure 屬性,否則不會。預設情況下,不會設定 Secure 屬性。

注意 將此設定為 true 時要小心,因為如果瀏覽器沒有 HTTPS 連線,相容的用戶端將不會在未來將 cookie 傳回伺服器。

請注意,secure: true建議 的選項。但是,它需要啟用 HTTPS 的網站,亦即安全 cookie 需要 HTTPS。如果設定 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" 設定正確設定以簡化開發與實際環境設定時,這會很有用。

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'
}))
名稱

要設定在回應中的工作階段 ID cookie 的名稱(並從請求中讀取)。

預設值為 'connect.sid'

注意 如果您有多個應用程式執行在同一個主機名稱上(這只是名稱,例如 localhost127.0.0.1;不同的方案和埠不會命名不同的主機名稱),那麼您需要將各個工作階段 cookie 分開。最簡單的方法就是為每個應用程式設定不同的 名稱

代理

設定安全 cookie 時信任反向代理(透過「X-Forwarded-Proto」標頭)。

預設值為 undefined

  • true 將會使用「X-Forwarded-Proto」標頭。
  • false 忽略所有標頭,並且只有在有直接 TLS/SSL 連線時才將連線視為安全。
  • undefined 使用 express 中的「信任代理」設定
重新儲存

強制將工作階段儲存回工作階段儲存,即使工作階段在請求期間從未修改過。這可能視您的儲存而定,但它也可能產生競爭狀況,其中一個客戶端對您的伺服器提出兩個平行請求,並且在一個請求中對工作階段所做的變更可能會在另一個請求結束時被覆寫,即使它沒有進行任何變更(此行為也取決於您使用的儲存)。

預設值為 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 物件新增至 session 以供使用,這會被視為對 session 的修改,導致 session 被儲存。此問題已在 PassportJS 0.3.0 中修正

secret

必填選項

這是用來簽署 session ID cookie 的密碼。它可以是單一密碼的字串,或多個密碼的陣列。如果提供的是密碼陣列,只有第一個元素會用來簽署 session ID cookie,而所有元素都會在驗證要求中的簽章時被考慮。密碼本身不應易於被人類解析,最好是一組隨機字元。最佳實務範例可能包括

  • 使用環境變數來儲存密碼,確保密碼本身不存在於您的儲存庫中。
  • 定期更新密碼,同時確保前一個密碼在陣列中。

使用無法猜測的密碼將降低劫持 session 的可能性,僅限於猜測 session ID(由 genid 選項決定)。

變更密碼值會使所有現有的 session 失效。若要在不使 session 失效的情況下輪替密碼,請提供一個密碼陣列,其中新密碼為陣列的第一個元素,並將先前的密碼包含在後面的元素中。

store

session 儲存體實例,預設為新的 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 的別名,且無法修改。已新增此屬性,以便從 session 物件存取會話 ID。

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 應以 callback(error) 呼叫。

store.clear(callback)

選用

此選用方法用於從儲存區中刪除所有工作階段。清除儲存區後,callback 應以 callback(error) 呼叫。

store.length(callback)

選用

此選用方法用於取得儲存區中所有工作階段的數量。callback 應以 callback(error, len) 呼叫。

store.get(sid, callback)

必要

此必要方法用於根據工作階段 ID (sid) 從儲存區中取得工作階段。callback 應以 callback(error, session) 呼叫。

如果找到 session 引數,應為工作階段;否則,如果未找到工作階段(且無錯誤),則為 nullundefined。當 error.code === 'ENOENT' 時,會特別處理,以執行類似 callback(null, null) 的動作。

store.set(sid, session, callback)

必要

此必要方法用於根據工作階段 ID (sid) 和工作階段 (session) 物件,將工作階段上傳或插入儲存區中。工作階段設定在儲存區中後,callback 應以 callback(error) 呼叫。

store.touch(sid, session, callback)

建議

此建議方法用於在給定會話 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 Table Storage 的會話儲存。

★ 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 基於 memcached 的會話儲存,使用 memjs 作為 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 使用原生 MySQL 透過 node-mysql 模組的會話儲存體。

★ express-nedb-session 基於 NeDB 的會話儲存體。

★ express-oracle-session 使用原生 oracle 透過 node-oracledb 模組的會話儲存體。

★ 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 模組記錄會話操作的資訊。

若要查看所有內部記錄,請在啟動應用程式時將 DEBUG 環境變數設定為 express-session(在此範例中為 npm start

$ DEBUG=express-session npm start

在 Windows 上,請使用對應的指令;

> set DEBUG=express-session & npm start

授權

MIT