アプリケーションにおけるユーザー認証は、セキュリティとユーザーエクスペリエンスの核となる部分です。
この記事では、セッションベース認証(この記事ではセッション認証とします)の基本を理解し、Go言語(Golang)のを使った具体的な実装方法を解説します。標準ライブラリのみ使用してシンプルに書いてあるのでGo言語を普段使わない人も理解しやすいかと思います。
セッション認証とは?
セッション認証は、ユーザーが一度ログインすると、その後の各リクエストでユーザーを識別する方法です。
通常、セッション認証では以下のステップが含まれます。
- ユーザーはブラウザを通じてIDとパスワードを含むログインリクエストをサーバーに送信します。
- サーバーは提供された認証情報を検証し、正しいと判断された場合にセッションを確立します。セッション情報はサーバー内とCookieに保存されます。今回はメモリに保存しますが、実際のアプリケーションではDBやその他のストレージを使用することが一般的です。
- サーバーは、セッションが確立されたことを示すレスポンスをブラウザに送り返します。
- ユーザーが認証済みであることを示すセッション情報を含むリクエストを、ブラウザはサーバーに送信します。
- サーバーはリクエストに含まれるセッション情報を検証し、ユーザーの認可を行います。
- 認可が成功し、ユーザーが特定のアクションやリソースへのアクセスを許可されたことを示すレスポンスをサーバーがブラウザに送ります。
- ユーザーがログアウトを要求すると、ブラウザはログアウトリクエストをサーバーに送信します。
- サーバーはユーザーのサーバー内とCookieからセッションを削除し、セッション情報を無効化します。
- サーバーは、セッションが正常に終了したことを示すレスポンスをブラウザに送り返します。
ブラウザ サーバー
| |
|--- 1. ログイン ------> |
| (IDとパスワード) |
| | 2. セッションの確立(サーバー内とCookieに保存)
| |
|<-- 3. レスポンス----- |
| |
| |
| --- 4. リクエスト ----->|
| (セッション情報含む) | 5.認可成功
| |
|<-- 6. レスポンス ----- |
| (ユーザー識別) |
| |
| --- 7. ログアウト ------>|
| | 8.セッション削除
|<-- 9. レスポンス --- |
| (セッション情報消去) |
| |
Go言語によるセッション認証のサンプル実装
以下は、上記で説明したセッション認証の基本的な流れを実装しました。
package main
import (
"fmt"
"net/http"
"sync"
)
var (
// セッション情報を保存するためのマップ
sessions = make(map[string]bool)
// セッション情報へのアクセスを同期するためのミューテックス
sessionMutex = &sync.Mutex{}
)
func main() {
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/dashboard", dashboardHandler)
http.HandleFunc("/logout", logoutHandler)
http.ListenAndServe(":8080", nil)
}
func loginHandler(w http.ResponseWriter, r *http.Request) {
// 本来はIDとパスワードでのユーザー認証のロジックを実装する
// セッションIDを生成(ここではシンプルにユーザー名を使用)
sessionID := "user123" // 実際にはもっと安全なランダムなIDを生成する
// サーバーサイドでセッションを保存
sessionMutex.Lock()
sessions[sessionID] = true
for id := range sessions {
fmt.Println("loginHandler: Current session ID is ", id)
}
sessionMutex.Unlock()
// クライアントにセッションIDをCookieとして送信
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
MaxAge: 60, // 有効期限を60秒(1分)に設定
})
w.Write([]byte("login successfully!"))
}
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
// CookieからセッションIDを取得
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
sessionMutex.Lock()
authenticated, ok := sessions[cookie.Value]
if ok {
for id := range sessions {
fmt.Println("dashboardHandler: Current session ID is ", id)
}
}
sessionMutex.Unlock()
// セッションが有効かチェック
if !ok || !authenticated {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
w.Write([]byte("Welcome to your dashboard!"))
}
func logoutHandler(w http.ResponseWriter, r *http.Request) {
// CookieからセッションIDを取得し、サーバーサイドでセッションを削除
cookie, err := r.Cookie("session_id")
if err == nil {
sessionMutex.Lock()
delete(sessions, cookie.Value)
if len(sessions) == 0 {
fmt.Println("logoutHandler: Current session ID is nothing")
}
sessionMutex.Unlock()
}
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: "",
Path: "/",
MaxAge: -1, // Cookieを削除
})
w.Write([]byte("Logout successful!"))
}
実際に動かす
まずはサーバーを起動
go run ./main.go
ログイン
そして、ブラウザで http://localhost:8080/login
にアクセスします。
アクセス成功です!
この時、サーバーではメモリにセッション情報を保持していることがわかります。
loginHandler: Current session ID is user123
ブラウザの開発者ツールでCookieを確認すると、セッションがセットされているのがわかります。
また、有効期間も設定されていますね。UTCで表示されているので、9時間の差があります。
有効期間を過ぎてブラウザをリロードすると、こちらが削除されていることが確認できます。
ダッシュボードへアクセス
次に、http://localhost:8080/dashboard
にアクセスします。
もし、ここでForbidden
が表示されている場合、有効期限が切れているので再度ログインしてください。
この時、サーバーではメモリにセッション情報を保持していることがわかります。
dashboardHandler: Current session ID is user123
ログアウト
最後に、http://localhost:8080/logout
にアクセスします。
これで、Cookieに保存されたセッションが削除されているのがわかります。
この時、サーバーではメモリにセッション情報を保持していることがわかります。
logoutHandler: Current session ID is nothing
この状態で、http://localhost:8080/dashboard
にアクセスします。
すると、セッションがないためエラーとなります。
まとめ
セッション認証はWebアプリケーションにおける重要な部分であり、Go言語を使って効果的に実装することができます。
商用で開発する際には、適切なセキュリティ対策を講じ、ユーザーに安全で快適な体験を提供しましょう。
参考