2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめてのアドベントカレンダーAdvent Calendar 2023

Day 2

改めて学ぶセッションベース認証[Goによる実例付き]

Last updated at Posted at 2023-11-25

アプリケーションにおけるユーザー認証は、セキュリティとユーザーエクスペリエンスの核となる部分です。
この記事では、セッションベース認証(この記事ではセッション認証とします)の基本を理解し、Go言語(Golang)のを使った具体的な実装方法を解説します。標準ライブラリのみ使用してシンプルに書いてあるのでGo言語を普段使わない人も理解しやすいかと思います。

セッション認証とは?

セッション認証は、ユーザーが一度ログインすると、その後の各リクエストでユーザーを識別する方法です。
通常、セッション認証では以下のステップが含まれます。

  1. ユーザーはブラウザを通じてIDとパスワードを含むログインリクエストをサーバーに送信します。
  2. サーバーは提供された認証情報を検証し、正しいと判断された場合にセッションを確立します。セッション情報はサーバー内とCookieに保存されます。今回はメモリに保存しますが、実際のアプリケーションではDBやその他のストレージを使用することが一般的です。
  3. サーバーは、セッションが確立されたことを示すレスポンスをブラウザに送り返します。
  4. ユーザーが認証済みであることを示すセッション情報を含むリクエストを、ブラウザはサーバーに送信します。
  5. サーバーはリクエストに含まれるセッション情報を検証し、ユーザーの認可を行います。
  6. 認可が成功し、ユーザーが特定のアクションやリソースへのアクセスを許可されたことを示すレスポンスをサーバーがブラウザに送ります。
  7. ユーザーがログアウトを要求すると、ブラウザはログアウトリクエストをサーバーに送信します。
  8. サーバーはユーザーのサーバー内とCookieからセッションを削除し、セッション情報を無効化します。
  9. サーバーは、セッションが正常に終了したことを示すレスポンスをブラウザに送り返します。
ブラウザ                   サーバー
  |                         |
  |--- 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 にアクセスします。
Screenshot 2023-11-25 at 13.43.18.png
アクセス成功です!
この時、サーバーではメモリにセッション情報を保持していることがわかります。

loginHandler: Current session ID is  user123

ブラウザの開発者ツールでCookieを確認すると、セッションがセットされているのがわかります。
また、有効期間も設定されていますね。UTCで表示されているので、9時間の差があります。
有効期間を過ぎてブラウザをリロードすると、こちらが削除されていることが確認できます。
Screenshot 2023-11-25 at 14.50.35.png

ダッシュボードへアクセス

次に、http://localhost:8080/dashboard にアクセスします。
Screenshot 2023-11-25 at 13.46.27.png
もし、ここでForbiddenが表示されている場合、有効期限が切れているので再度ログインしてください。

この時、サーバーではメモリにセッション情報を保持していることがわかります。

dashboardHandler: Current session ID is  user123

ログアウト

最後に、http://localhost:8080/logout にアクセスします。
Screenshot 2023-11-25 at 13.49.39.png

これで、Cookieに保存されたセッションが削除されているのがわかります。
Screenshot 2023-11-25 at 13.50.15.png
この時、サーバーではメモリにセッション情報を保持していることがわかります。

logoutHandler: Current session ID is nothing

この状態で、http://localhost:8080/dashboard にアクセスします。
すると、セッションがないためエラーとなります。
Screenshot 2023-11-25 at 13.56.09.png

まとめ

セッション認証はWebアプリケーションにおける重要な部分であり、Go言語を使って効果的に実装することができます。
商用で開発する際には、適切なセキュリティ対策を講じ、ユーザーに安全で快適な体験を提供しましょう。

参考

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?