ExpressでSessionを利用する方法

ExpressでSessionを扱う方法について取り上げます。「express-session」「cookie-parser」をMiddlewareに指定してSessionを扱えるようにします。今回、Session情報はRedisで管理します。

前準備

Redisを起動

今回、セッション情報はRedisで管理します。

docker-composeでRedis立ち上げます。docker-compose.yml に以下内容を記述します。

version: '3'

services:
  redis:
    image: "redis:latest"
    ports:
      - "6379:6379"
    volumes:
      - "./data/reis:/data"

dockerを立ち上げます。

$ docker-compose up -d

Redisのデータを確認します。

$ docker-compose ps
         Name                        Command               State           Ports         
-----------------------------------------------------------------------------------------
express_session_redis_1   docker-entrypoint.sh redis ...   Up      0.0.0.0:6379->6379/tcp
$ docker exec -it express_session_redis_1 /bin/bash
root@8ab82951a477:/data# 
root@8ab82951a477:/data# redis-cli
127.0.0.1:6379> 
127.0.0.1:6379> keys *
(empty list or set)

パッケージインストール

必要なパッケージをインストールします。

npm install --save express \
express-session \
cookie-parser \
redis \
connect-redis
  • cookie-parser
    • セッションIDと呼ばれる値がCookieに保存されます。
    • クライアントからサーバに再アクセスする際、リクエストヘッダのCookieにセッションIDを指定します。
    • リクエストヘッダに指定されたCookieを読み取るために cookie-parser が必要となります。
    • サーバ側ではCookieから読み取ったセッションIDを利用してセッション情報にアクセスします。
  • redis connect-redis
    • 今回セッション情報をRedisで管理するため必要となります。

動作確認用コード

app.js というファイルを作成して以下処理を記述します。

const express = require('express')

const cookieParser = require('cookie-parser')
const session = require('express-session')
const redis = require('redis')

const app = express()

const RedisStore = require('connect-redis')(session)
const redisClient = redis.createClient()

app.use(cookieParser())
app.use(session({
  secret: 'secret_key',
  resave: false,
  saveUninitialized: false,
  store: new RedisStore({ client: redisClient }),
  cookie: { httpOnly: true, secure: false, maxage: 1000 * 60 * 30 }
}))

app.get('/', (req, res) => {
  if (req.session.views) {
    req.session.views++
  } else {
    req.session.views = 1
  }
  res.json({
    'sessionID': req.sessionID,
    'req.session.views': req.session.views
  })
})

app.listen(5000, () => console.log('Example app listening on port 5000!'))

req.session でセッション情報にアクセスできます。

同一セッションでアクセスされるたびに req.session.views がインクリメントされていく処理になっています。

動作確認

Express起動

Expressを起動します。

$ node app.js 
Example app listening on port 5000!

curlでアクセス

$ curl -v http://localhost:5000 | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 70
< ETag: W/"46-epFTMNEhE6iPJMaUVafCPXk7BKk"
< Set-Cookie: connect.sid=s%3AuNPBmH4CKYJhxwuuE8fR5NCg0q2M4LBT.KICNGChskL9c7%2BH5mz6ju33lEUlJwYuds255QTY4wDo; Path=/; HttpOnly
< Date: Fri, 21 Dec 2018 01:58:00 GMT
< Connection: keep-alive
< 
{ [69 bytes data]
100    70  100    70    0     0   5000      0 --:--:-- --:--:-- --:--:--  5000
* Connection #0 to host localhost left intact
* Closing connection 0
{
  "sessionID": "uNPBmH4CKYJhxwuuE8fR5NCg0q2M4LBT",
  "req.session.views": 1
}
$ curl -v http://localhost:5000 | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 70
< ETag: W/"46-DCWS9uO2AuRzE+bDZSV1kylvVrc"
< Set-Cookie: connect.sid=s%3AR_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2.u5wCq%2FfUPqtSwShIempPgXLm%2F%2FULISnTTZtu%2F47rXAI; Path=/; HttpOnly
< Date: Fri, 21 Dec 2018 01:59:43 GMT
< Connection: keep-alive
< 
{ [69 bytes data]
100    70  100    70    0     0   7000      0 --:--:-- --:--:-- --:--:--  7000
* Connection #0 to host localhost left intact
* Closing connection 0
{
  "sessionID": "R_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2",
  "req.session.views": 1
}

上記2回のリクエストでは、Cookieにセッションを指定せずにリクエストしています。
そのため、サーバ側で新たに2つのセッションデータが作られています。

次に、さきほどのリクエストで生成された以下のセッションをCookieに指定してアクセスします。

connect.sid = s%3AR_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2.u5wCq%2FfUPqtSwShIempPgXLm%2F%2FULISnTTZtu%2F47rXAI
$ curl -b connect.sid=s%3AR_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2.u5wCq%2FfUPqtSwShIempPgXLm%2F%2FULISnTTZtu%2F47rXAI -v http://localhost:5000 | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET / HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.64.1
> Accept: */*
> Cookie: connect.sid=s%3AR_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2.u5wCq%2FfUPqtSwShIempPgXLm%2F%2FULISnTTZtu%2F47rXAI
> 
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
< Content-Length: 70
< ETag: W/"46-u9eZecG7jG3vaA3OvXUb5eImPKw"
< Date: Fri, 21 Dec 2018 02:05:01 GMT
< Connection: keep-alive
< 
{ [69 bytes data]
100    70  100    70    0     0   1521      0 --:--:-- --:--:-- --:--:--  1521
* Connection #0 to host localhost left intact
* Closing connection 0
{
  "sessionID": "R_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2",
  "req.session.views": 2
}

すでに存在するセッションのため req.session.views の値が2になっています。

保存されたセッションデータを確認

Redisに保存されたセッションデータを確認します。

127.0.0.1:6379> keys *
1) "sess:R_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2"
2) "sess:uNPBmH4CKYJhxwuuE8fR5NCg0q2M4LBT"
127.0.0.1:6379> 
127.0.0.1:6379> get sess:R_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2
"{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"secure\":false,\"httpOnly\":true,\"path\":\"/\"},\"views\":2}"
127.0.0.1:6379> 
127.0.0.1:6379> get sess:uNPBmH4CKYJhxwuuE8fR5NCg0q2M4LBT
"{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"secure\":false,\"httpOnly\":true,\"path\":\"/\"},\"views\":1}"

2つセッションデータが存在しており、 sess:R_mJz2bz_vR1RnqBBqSP4nzzefeS9DE2 のほうは views2 になっています。

参考