RailsでセッションとCookieを操作する方法

Ruby

セッションとはステートレスなHTTP通信においてステートフルを実現するための情報、Cookieとはブラウザに用意されたデータの保存領域のことをいいます。
セッションの保存先にはいくつか候補がありますが、例えばセッションをCookieに保存することでステートフルなHTTP通信が実現できます。

今回はRailsアプリケーションでセッションとCookieを操作する方法について紹介します。なお、Railsアプリケーションはモノリス、つまりAPIモードで作成していない前提となっています。

基礎知識について

ステートレスとステートフルについて

ステートレスは現在の状態を保持しない、ステートフルは現在の状態を保持することをいいます。

ステートレスのイメージは以下の通りです。ステートレスでは状態が保持されないため、やり取りをするたびに逐一情報を確認する必要があります。

ステートフルのイメージは以下の通りです。ステートフルでは状態が保持されるため、過去の情報をもとにしたやり取りが可能です。

RailsによるCookieの操作方法

Cookieの作成方法

今回は以下のCookieの作成方法について紹介します。

  • セッションCookie(Session cookies)
  • 持続的Cookie(Permanent cookies)
  • 署名付きCookie(Signed cookies)
  • 暗号化Cookie(Encrypted cookies)

セッションCookie(Session cookies)の作成方法

セッションCookie(Session cookies)とはセッションと一緒に削除されるCookieです。ブラウザを終了するとセッションCookieは削除されますが、ブラウザによっては再起動時にセッションCookieが復元されるものもあります。1
セッションCookieは一時Cookieと呼ばれることもあります。2

RailsアプリケーションでCookieを操作する場合はAction Controllerのcookiesメソッドを利用します。3

セッションCookieはcoookie[キー名] = 値で作成します。
例えば以下のようなControllerにアクセスするとSet-Cookie: user_id=1; path=/がヘッダにセットされたレスポンスが返ってきます。

def index
  cookie[:user_id] = 1
end

ブラウザのCookieを確認すると先ほど定義したキーと値がセットされていることがわかります。

持続的Cookie(Permanent cookies)の作成方法

持続的Cookie(Permanent cookies)とはExpires属性の時刻、もしくはMax-Ageの期間が経過した後に削除されるCookieです。1
持続的Cookieは持続Cookie、永続的Cookie、永続Cookie、Persistent cookiesなどと呼ばれることもあります。4 5 2

持続的Cookieはcookies.permanent[キー名] = 値で作成します。
例えば以下のようなControllerにアクセスするとSet-Cookie: user_id=1; path=/; expires=Fri, 31 Jan 2042 12:56:41 GMTがヘッダにセットされたレスポンスが返ってきます。

def index
  cookies.permanent[:user_id] = 1
end

ブラウザのCookieを確認すると先ほど定義したキーと値がセットされていることがわかります。

署名付きCookie(Signed cookies)の作成方法

署名付きCookieとはsecrets.secret_key_baseによって暗号化されたCookieのことをいいます。第三者は署名付きCookieを改ざんできません。3

署名付きCookieはcookies.signed[キー名] = 値で作成します。
例えば以下のようなControllerにアクセスするとSet-Cookie: user_id=暗号化された値; path=/がヘッダにセットされたレスポンスが返ってきます。

def index
  cookies.signed[:user_id] = 1
end

ブラウザのCookieを確認すると先ほど定義したキーと暗号化された値がセットされていることがわかります。

暗号化Cookie(Encrypted cookies)の作成方法

暗号化Cookieとはsecrets.secret_key_baseによって暗号化されたCookieのことをいいます。第三者は暗号化Cookieの改ざんだけでなく読み込みもできません。3

暗号化Cookieはcookies.encrypted[キー名] = 値で作成します。
例えば以下のようなControllerにアクセスするとSet-Cookie: user_id=暗号化された値; path=/がヘッダにセットされたレスポンスが返ってきます。

def index
  cookies.encrypted[:user_id] = 1
end

ブラウザのCookieを確認すると先ほど定義したキーと暗号化された値がセットされていることがわかります。

オプション付きでCookieを作成する方法

Cookieはハッシュ形式でも作成できます。ハッシュ形式を利用したCookieの作成ではオプションが設定できます。
Cookie作成時のオプションは以下の通りです。6

オプション 説明 デフォルト
:value
:path 有効なパス 現在のホスト
:domain 有効なドメイン 現在のホスト
:expires 有効期限 /
:secure 暗号化通信でのみCookieを送信 false
:httponly HTTPCookieを有効 false

Domain属性とはCookieを受信できるホストを指定するオプションです。
Secure属性とはHTTPS通信の時のみサーバへCookieの送信を許可するオプションです。
HttpOnly属性とはJavaScriptによるCookie操作をできなくするオプションです。

例えば以下のようなControllerにアクセスするとSet-Cookie: user_id=1; path=/; expires=Thu, 10 Feb 2022 00:00:00 GMTがヘッダにセットされたレスポンスが返ってきます。

def index
  cookies[:user_id] = {
    value: 1,
    expires: "2022-02-10".to_date
  }
end

Cookieの取得方法

coookie[キー名]でCookieの値を取得できます。
例えば、user_idという値が1のCookieを取得する方法は以下の通りです。

   2: def index
 => 3:   binding.pry
    4: end

> cookies[:user_id]
=> "1"

署名付きCookieの場合はcoookie[キー名]で暗号化された値、cookies.signed[キー名]で復号された値が取得できます。具体例は以下の通りです。

   2: def index
 => 3:   binding.pry
    4: end

# cookies[キー名]で取得すると暗号化された値がそのまま取得される
> cookies[:user_id]
=> "eyJfcmFpbHMiOn..."

# cookies.signed[キー名]で取得すると復号された値が取得できる
> cookies.signed[:user_id]
=> 1

暗号化Cookieの場合はcoookie[キー名]で暗号化された値、cookies.encrypted[キー名]で復号された値が取得できます。具体例は以下の通りです。

   2: def index
 => 3:   binding.pry
    4: end

# cookies[キー名]で取得すると暗号化された値がそのまま取得される
> cookies[:user_id]
=> "genL3QVB..."

# cookies.encrypted[キー名]で取得すると復号された値が取得できる
> cookies.encrypted[:user_id]
=> 1

Cookieの削除方法

Cookieはcookies.delete キー名で削除します。
例えば、user_idという値が1のCookieを削除する方法は以下の通りです。

def index
  cookies.delete(:user_id)
end

Railsによるセッションの操作方法

セッションの取得方法

セッションの情報はActionDispatch::Request::Sessionクラスのsessionというオブジェクトに保存されています。7
セッション内ではキーによって情報が管理されています。キーの一覧はsession.keysで、値の一覧はsession.valuesで、該当するキーの値はsession[キー名]で取得します。

From: /app/app/controllers/home_controller.rb:3 HomeController#index:

     2: def index
 =>  3:   binding.pry

> session.keys
=> ["session_id", "_csrf_token"]

> session.values
=> ["0d01dbf0e2e3913395d2daffd31c2a0d", "IUZ2mUVfDWopqh3+SKnE9wmyv2iubqh4QHAIqwtWUEk="]

> session[:session_id]
=> "0d01dbf0e2e3913395d2daffd31c2a0d"

参考: Railsアプリケーションのセッションの保存先について

sessionオブジェクトで管理されているRailsアプリケーションのセッションはデフォルトでCookieに保存されます。キー名は_アプリケーション名_sessionです。ここでいうアプリケーション名とはconfig/application.rbのmodule名を指します。Cookieに保存されるRailsアプリケーションのセッションは暗号化された状態になっています。

なお、Railsのセッションの保存先をCookieからRedisに変更した場合は当該CookieにはセッションIDのみが保存されます。

    2: def index
 => 3:   binding.pry
    4: end

[1] pry(#<HomeController>)> session[:session_id]
=> "fe1c7de48765b8014c8be95a0fd9239f"

Railsアプリケーションのセッションの保存先の変更方法の詳細解説はRailsのセッション保存場所をCookieからRedisに変更する方法で紹介しています。

セッションの作成方法

session[キー名] = 値でセッションを作成します。

From: /app/app/controllers/home_controller.rb:3 HomeController#index:

     2: def index
 =>  3:   binding.pry

### セッション作成前
> session.keys
=> ["session_id", "_csrf_token"]

> session.values
=> ["be4c69c62729186d9fd61777aab12b4e", "mHVWDCRzUjYjHCAIZ8JEvx+/vb1nfDb0RUkeumJ7Pgs="]

### セッション作成
> session[:user_id] = 1

### セッション作成後
> session.keys
=> ["session_id", "_csrf_token", "user_id"]

> session.values
=> ["be4c69c62729186d9fd61777aab12b4e", "mHVWDCRzUjYjHCAIZ8JEvx+/vb1nfDb0RUkeumJ7Pgs=", 1]

> session[:user_id]
=> 1

セッションの削除方法

session[キー名] = nilでセッションを削除します。

From: /app/app/controllers/home_controller.rb:3 HomeController#index:

     2: def index
 =>  3:   binding.pry

### セッション削除前
> session.keys
=> ["session_id", "_csrf_token", "user_id"]

> session.values
=> ["be4c69c62729186d9fd61777aab12b4e", "mHVWDCRzUjYjHCAIZ8JEvx+/vb1nfDb0RUkeumJ7Pgs=", 1]

> session[:user_id]
=> 1

### セッション削除
> session[:user_id] = nil

### セッション削除後
> session.keys
=> ["session_id", "_csrf_token", "user_id"]

> session.values
=> ["be4c69c62729186d9fd61777aab12b4e", "mHVWDCRzUjYjHCAIZ8JEvx+/vb1nfDb0RUkeumJ7Pgs=", nil]

> session[:user_id]
=> nil

あるいは、sessionはハッシュオブジェクトであるためsession.delete(キー名)でも全てのセッションを削除できます。

From: /app/app/controllers/home_controller.rb:3 HomeController#index:

     2: def index
 =>  3:   binding.pry

### セッション削除前
> session.keys
=> ["session_id", "_csrf_token", "user_id"]

> session.values
=> ["02a0411f332a63d221e6554b5984fa92", "KE/RiKhAOS9EQkiv19QbXjK9iG/ZWP5MggT5Jqyk2nc="]

> session[:user_id]
=> 1

### セッション削除
> session.delete(:user_id)

### セッション削除後
> session.keys
=> ["session_id", "_csrf_token"]

> session.values
=> ["02a0411f332a63d221e6554b5984fa92", "KE/RiKhAOS9EQkiv19QbXjK9iG/ZWP5MggT5Jqyk2nc="]

> session[:user_id]
=> nil

セッションのリセット方法

reset_sessionで全てのセッション情報を削除します。

From: /app/app/controllers/home_controller.rb:3 HomeController#index:

     2: def index
 =>  3:   binding.pry

### リセット前
> session.keys
=> ["session_id", "_csrf_token"]

> session.values
=> ["cf1307191e2dd9f713b57da47ba7e086", "qvWHoFgFX8NA0fsB/0Ha/Of+4d3xu2kVg3rvlaffS1s="]

### リセット
> reset_session

### リセット後
> session.keys
=> ["session_id"]

> session.values
=> ["6fd19009348e89543e7f76fdee60287f"]

あるいは、sessionはハッシュオブジェクトであるためsession.clearでも全てのセッションを削除できます。

From: /app/app/controllers/home_controller.rb:3 HomeController#index:

     2: def index
 =>  3:   binding.pry

### リセット前
> session.keys
=> ["session_id", "_csrf_token"]

> session.values
=> ["cf1307191e2dd9f713b57da47ba7e086", "qvWHoFgFX8NA0fsB/0Ha/Of+4d3xu2kVg3rvlaffS1s="]

### リセット
> session.clear

### リセット後
> session.keys
=> []

> session.values
=> []

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料