ログイン情報保持について理解する（ステートレス、ステートフル）

## 目次
- ログイン情報はどのように保存されている？
  - ステートレスの概念とは
  - ステートフルの概念とは
- HTTP通信の仕組み
    - ①リクエストに含まれるuser_idを頼りにする
    - ②情報を暗号化してブラウザのクッキーのみで保持する
- その他のセッション管理方法
- セッション管理まとめ

## ログイン情報はどのように保存されている？
ログイン情報などを保存する際に重要になる「ステートレスな通信、ステートフルな通信」という概念について解説をします。

### ステートレスの概念とは
HTTP通信は **「ステートレス」** な通信です。
これは 前の通信を引き継がない、 **１つの通信が独立している** という意味です。
ステートレスの概念を理解するために、 マクドナルドでポテトを注文する ケースを想定してみましょう。

![1059a5f526dd6faed3eaf0713c814c1c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/43eac67e-8d43-d8bf-fd56-487bcf90f52b.png)

なぜ途中で会話が成立しなくなるのでしょうか？
ステートレスでは **前の通信（会話）の引き継ぎを行いません。** 

そのため店員は、 Mサイズで という注文をした時点では、先ほどの ポテトくださいという情報を失っているのです。

ステートレス通信は前の通信状態は引き継がないため、 **誰がどのような状態かを保持しない** 仕組みであると言えます。

上記の会話が成立するためにはどうすれば良いでしょうか？

答えは簡単で、 **「前の通信（会話）を引き継げば良い」** です。

![af4a2cfe29dd512bb76d5f3cfcadfe58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/8cc89981-568e-1908-f87d-bfe054908f59.png)


上記のように客が「ポテトください」と注文した時点で、店員が **ポテトが注文されたことを認識** します。

そうすることで客が「Mサイズ」と注文した時に店員がなんのことだか認識することができるのです。


当たり前のような注文の流れですが、このように **前の通信を引き継ぐことで誰がどのような状態かを保持する** ことができます。

このような通信のことを **ステートフル** と言います。

## HTTP通信の仕組み
では上記の例えを実際のHTTP通信に置き換えてみましょう。

HTTP通信は基本ステートレスな通信ですが、場合によってはステートフルにしないと困る時があります。

例えば以下のように DBには保存しないが、ページを超えて保持しておきたい値 が存在する場合です。

- ネット通販のカートの中身
- ログインしているユーザ情報

ステートフルな通信を実現するために **セッション管理** という仕組みがあります。

セッション管理には様々な方法がありますが、ここでは２つ紹介します。

### ①リクエストに含まれるuser_idを頼りにする
初めてアクセスするユーザがリクエストを出します。

![c6802d08d94734e22b13f14f30ba0a1b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/8a7bec47-b66c-1188-f98b-e3303aa745d6.png)


コンピュータ側がアクセスしたユーザを初めてであると認識します。


![15976cf24b48641f7aa80ef43751e410.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/b3117520-db15-0c54-5649-a52ca0c926f5.png)

コンピュータ側にアクセスしたユーザを覚えるための領域が作成されます。

この領域は **セッション領域** と呼ぶことにします。

セッション領域は **user_id** という番号で管理されます。

セッション領域で管理する値は **セッション情報** と呼ばれます。

![c7232a101935ebdcdfbbac88584054b4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/212978f7-a4ad-50d9-4aad-783ed5162bd8.png)

レスポンスと一緒に **user_id** が送られ、ブラウザ側の **クッキー** というテキストファイルで保持されます。

![536a9bde03477f1c837afec15fac4649.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/1a1be01a-d721-17ff-62b1-f6ffba455831.png)
![07b5e40b90c365288252f05674925ee0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/5c93bfaf-5332-178b-c856-38014a6d0456.png)

2回目以降アクセスする時は **アクセスしたユーザ** を **コンピュータ側** と **ブラウザ側** の双方が認識している状態になっています。
リクエストと一緒に **user_id** の情報が乗ったクッキーと、コンピュータ側で保持したい値がコンピュータへ送られます。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/696a494c-0a60-8638-6c9a-71302bafdafe.png)

送られたuser_idとコンピュータ側のセッション領域に書いてある **user_id** を比較して、セッション情報が適切なセッション領域に送られます。

![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/803b2f03-2cdd-bb82-5c4a-3adad9e2185e.gif)


その結果、以下のようなステートフルな通信を実現できます。

[![Image from Gyazo](https://t.gyazo.com/teams/diveintocode/1e348f216235b1f2692c2655c93a868e.gif)](https://diveintocode.gyazo.com/1e348f216235b1f2692c2655c93a868e)


### ②情報を暗号化してブラウザのクッキーのみで保持する
初めてアクセスするユーザがリクエストを出します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/75807972-908b-f596-8e0b-38c4a431855a.png)

コンピュータ側がアクセスしたユーザを初めてであると認識します。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/64bac17c-beee-a6e8-3a9d-0bdf49ebf452.png)


初めてのユーザには **user_id** というキーで、 **そのユーザのid** を代入します。

次に user_id = 1 というセッション情報を **シークレットキー** という鍵で暗号化してクッキーを生成します。

シークレットキーとは **コンピュータ内で暗号化/復号化するための鍵** です。

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/067a71c6-9320-3548-3535-9e3cff91080a.png)


レスポンスと一緒に **セッション情報が暗号化されたクッキー** が送られ、ブラウザ側で保持されます。

クッキーにセッション情報のうちuser_idのみを持たせる方式が一般的です。

なぜならクッキーは **最大で4KB** の情報しか保持することができないからです。



この **クッキーにセッション情報を全て持たせる** 方式は、Railsのデフォルトの設定となっています。

ブラウザで保持されているクッキーに記載されているセッション情報が暗号化されているのは、

ブラウザの検証ツールで **クッキーの情報を書き換えられないようにするため** です。

もし暗号化しないと、ログインしているユーザのidを書き換えられて、ログインしているユーザが誰だか分からなくなってしまうからです。

さて現状は **アクセスしたユーザ** を **ブラウザ側** のみが認識している状態になっています。

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/0f4cfede-1695-707f-da0e-30d2be7d5d49.png)


２回目以降アクセスする時は、リクエストと一緒に **user_id** の情報が乗ったクッキーと、コンピュータ側で保持したい値がコンピュータへ送られます。

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/505f4b78-59dd-324f-b6f5-0f1ca18dba82.png)


送られたクッキーを復号化して **セッション情報** が存在していれば、ユーザを特定することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/b87892e4-9893-7406-bc5c-2a67067a5b1f.png)

認識したところで、セッション情報を追記して新たにクッキーを生成します。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/96842130-df8b-2ad6-338a-2bb8679aee05.png)


最後に再び暗号化して、新しいセッション情報を元にして作られたクッキーがレスポンスとして一緒に送られます。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/34157b47-643a-95cd-6186-ba83e5668a73.png)

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600373/715e4fa9-b1be-b0ca-6471-752db8c8401f.png)


その結果以下のようにブラウザ側でセッション情報が保持されます。

[![Image from Gyazo](https://t.gyazo.com/teams/diveintocode/68ef58b39f6b29ef7f7865a5cdd2ab3e.gif)](https://diveintocode.gyazo.com/68ef58b39f6b29ef7f7865a5cdd2ab3e)

このやり方をまとめると以下のようになります。


:::note info
- クッキーだけに全てのセッション情報を集約する
- クッキーを乗っ取られると全てが乗っ取られると言っても過言ではない
- コンピュータ側はセッション情報を何も把握していない
- セッション情報を暗号化してクッキーを生成するのは、クッキーを乗っ取られないようにするため
- コンピュータは復号化されたクッキーを見て、ユーザを認識できる能力がある

:::

つまりクッキーが持っているセッション情報を復号化して **誰がどのような状態か** を把握しているのです。

最後の **コンピュータは復号化されたクッキーを見て、ユーザを認識できる能力がある** に関してですが、

これはコンピュータ内部で生成されるロジックに起因し、深い知識が必要となるため、ここでは省略させていただきます。

## その他のセッション管理方法
先ほどセッション管理方法を２つ紹介しましたが、セッション管理は他にも方法があります。

- クッキーにセッション情報を保持させ、リクエストのuser_idを頼りにする
- クッキーにセッション情報を暗号化してブラウザに保持させる
- コンピュータ内のメモリにセッション情報を保持させる
- コンピュータ内のDBにセッション情報を保持させる
- ここではすべてを紹介しませんが、セッション管理は様々な方法があることを理解しましょう。

## セッション管理まとめ
- 誰がどのような状態かを保持しておきたい時には **ステートフル** にする必要がある
- 基本はステートレスなので、ステートフルにするためには **セッション管理** をする必要がある
- セッション管理をするためには **クッキー** や **セッション情報** が必要

ここでは4つの重要単語が出てきました。

最後に、これらの単語の意味を復習します。

- セッション管理：ステートフルにするための仕組み
- セッション情報：保持したい値
- クッキー：ブラウザ側でセッション情報を保持するテキストファイル
- シークレットキー：セッション情報をコンピュータ内で暗号化/復号化するための鍵

いかがでしたでしょうか？

セッション管理の方法は、アプリケーション制作において重要な知識ですので、一つずつ着実に理解していきましょう！

この技術についてさらに深く学びたい方は **1週間無料! [DIVER Learnings セッションログインシリーズ](https://onl.la/sVH4gad)** で学習できます。是非ご利用ください。