Goのgorilla/sessionsでセッションの実装を理解する
セッションを学ぶ機会があり、「そもそもセッションとは何か?」から始まりって、Goのnet/http
とgorilla/sessions
を使った簡単な実装までをしてみたので、要点だけまとめてみます!
ざっくりSessionとは?
仕組みだけを一言で言うと、HTTPリクエストを投げたユーザーデータをサーバーサイドに保存し、ユーザーにはIDを与えてユーザーを識別する機能のことです。
Sessionの使用例
具体的な例として以下の図で見てみましょう。Amazonのようなオンラインショッピングサイトを訪問するようなシチュエーションです。
https://www.seobility.net/en/wiki/Session_ID
-
期間を空けてユーザーがサイトにアクセスするとログインを要求されます。ログインに成功するとサーバーはユーザー情報を保持し、ユーザーに対してセッションIDを付与します。
-
このセッションIDはクッキーとしてユーザーのローカルに保存されます。クッキーにはユーザー固有の情報や有効期限などの情報が含まれています。🍪
-
クッキーの有効期限内にユーザーが再度サイトにアクセスすれば、前回アクセス時のセッションが保たれます。今回で言うと、ログインせずに直接アクセスできたり、前回カートに入れた商品を復元できたりといったことが可能です。
Session vs JWT
セッションとよく比較されるのがJWTです。JWTについては以下でまとめたのでよければ参考にしてください。
細かい違いやセキュリティ面は一旦置いておいて、一番はデータの保存場所が違うことです。
セッションはサーバー側でユーザー情報を保存するのに対し、JWTはユーザー(クライアント)側で保持します。
以下の動画が簡潔でわかりやすかったのでぜひ参考にしてみてください!
GoでSessionを実装してみる
実際にGoでSessionを実装する簡単な例で理解しましょう。
仕組みを理解すれば(多分)簡単で、ログインしてきたユーザーにセッションを設定して保存し、セッション情報をもつユーザーに対してはセッション情報をチェックする、といった流れです。
まずは必要なライブラリをインポートします。
package main
import (
"fmt"
"net/http"
"github.com/gorilla/sessions"
)
次に以下のようにセッションの初期設定をします。
// 管理するためのCookieStoreオブジェクトをstoreとして定義
var store = sessions.NewCookieStore([]byte("secret-key"))
const (
SessionKey = "session-key"
SessionName = "session-name"
)
次に、セッション情報をチェックする関数を実装します。
// セッションをチェックし、認証されたユーザーが存在する場合はそのユーザーを返す関数
// セッションが存在しないかユーザーが認証されていない場合は、403(Forbidden)を返す
func CheckSession(w http.ResponseWriter, r *http.Request) {
// 指定された名前を使用してセッションを取得
session, _ := store.Get(r, SessionName)
// セッションの値からユーザーを取得。型アサーションを使用して、値が文字列であることを確認
// アサーションが失敗した場合、okはfalse
user, ok := session.Values[SessionKey].(string)
if !ok {
// ユーザーが認証されていない場合は、許可されていないエラーを返す
http.Error(w, "許可されていない", http.StatusForbidden)
return
}
// ユーザーが認証されている場合は、認証されたユーザーをコンソールに表示
fmt.Fprintf(w, "認証されたユーザー: %v", user)
}
セッション情報を保存するログイン関数は以下のように実装します。
// SessionKeyのセッションの値を"session-id"に設定し、そのセッションを保存する関数
func login(w http.ResponseWriter, r *http.Request) {
// 指定された名前を使用してセッションを取得
session, _ := store.Get(r, SessionName)
// セッションの値に値を設定
session.Values[SessionKey] = "session-id"
// セッションを保存
session.Save(r, w)
}
最後に以下のようにmain()でルーティングすれば完成です。
func main() {
http.HandleFunc("/check", CheckSession)
http.HandleFunc("/login", login)
http.ListenAndServe(":8080", nil)
}
プログラムを実行してブラウザで確認してみます。
go run main.go
ブラウザでhttp://localhost:8080/login
にアクセスして以下が表示されることを確認してください。
次にhttp://localhost:8080/check
にアクセスしてセッションが有効になっていることを確認してください。
なお、セッションの有効期限などはOptionで付与することも可能ですので、公式ドキュメントなどを参考にして、仕様に合わせてカスタマイズしてみてください!
参照
Discussion