HttpOnly Cookie ベースのリフレッシュトークン、セッション管理、設定可能なセキュリティ設定を備えた、プロダクション志向のフルスタックデモアプリケーションです。安全な認証パターンを実装しています。
- 安全な認証:
- HttpOnly Cookie:リフレッシュトークンを HttpOnly Cookie に保存し、XSS 攻撃を防止します。
- セッション管理:アクセストークンは
sessionStorage(タブ固有)に保存されます。 - ブラックリスト:ログアウト時に Redis を使ってアクティブなトークンをブラックリスト化します。
- 環境ベースのセキュリティ:
- CORS:環境(dev / test / prod プロファイル)ごとに設定可能。
- Cookie 属性:
secureとSameSiteは環境ごとに異なります。 - シークレット:
JWT_SECRETは環境変数で管理します。
- バックエンド(Spring Boot):JWT トークンの発行、資格情報の検証、セッション状態の管理、セキュリティポリシーの適用、Redis でのトークンブラックリスト処理を担当します。
- フロントエンド(React):UI 状態を管理し、アクセストークンを sessionStorage に保存、401 エラー時にリフレッシュをトリガーし、ログイン/ログアウトの UI フローを処理します。
- Redis:即時失効のためのトークンブラックリストを保持し、アクティブセッションのセッションストアとして機能します。
- アクセストークン:有効期間は短く(1 時間)、API 認証に使用され、XSS アクセスを防ぐために sessionStorage に保存されます。
- リフレッシュトークン:有効期間は長く(7 日間)、新しいアクセストークンを取得するために使用され、XSS による窃取を防ぐために HttpOnly Cookie に保存されます。
localStorage ではなく sessionStorage を選んだ理由は、タブ固有でタブを閉じるとクリアされるためです。これにより、セッション間のセキュリティ分離が向上し、異なるブラウジングコンテキスト間でのトークン再利用リスクが低減します。また、多くのアプリケーションが期待する「ブラウザを閉じたらログアウト」という挙動も自動的に実現できます。
JWT は本来ステートレスですが、それでもセッションを直ちに失効させる機能が必要です(例:ユーザーがログアウトをクリックした、管理者がセッションを強制終了した、セキュリティ侵害を検知したなど)。Redis ベースのブラックリストにより、トークン検証時に O(1) ルックアップが可能となり、「ほぼステートレスな JWT」と「必要に応じた即時失効機能」の両立を実現できます。
- バックエンド:Spring Boot 3.x、Spring Security 6、JPA、Redis、MySQL/H2
- フロントエンド:React 18、TypeScript、Axios、Context API
- テスト:JUnit 5、MockMvc、TestContainers 対応
- Java 17 以上
- Node.js 18 以上
- Docker(任意、Redis/MySQL 用)
-
環境変数の設定: 必要な環境変数を設定します。
JWT_SECRETは必須で、32 バイト以上である必要があります。export JWT_SECRET=your_secure_random_secret_key_at_least_32_bytes export CORS_ALLOWED_ORIGINS=http://localhost:3000
必須環境変数:
JWT_SECRET:安全なランダムシークレットキー(32 バイト以上)CORS_ALLOWED_ORIGINS:フロントエンドアプリケーションのオリジン
任意環境変数:
JWT_EXPIRATION:アクセストークンの有効期限(ミリ秒、デフォルト:3600000)JWT_REFRESH_EXPIRATION:リフレッシュトークンの有効期限(ミリ秒、デフォルト:86400000)
-
Maven で実行:
cd backend ./mvnw spring-boot:run
-
環境変数の設定:
frontendディレクトリに.envファイルを作成します:REACT_APP_API_BASE_URL=http://localhost:8080/api
-
インストールと実行:
cd frontend npm install npm start
- ログイン:クライアントが資格情報を送信します。サーバーは検証し、以下を返します:
accessToken(JSON ボディ)→sessionStorageに保存。refreshToken(HttpOnly Cookie)→ ブラウザが自動的に処理。
- アクセス:クライアントが
Authorization: Bearer <token>ヘッダーを送信します。 - リフレッシュ:アクセストークンの有効期限が切れると(401)、クライアントは
/refreshを呼び出します。サーバーは Cookie を検証し、新しいアクセストークンを返します。 - ログアウト:クライアントが
/logoutを呼び出します。サーバーは Cookie を無効化し、Redis でアクセストークンをブラックリスト化します。
- Development:
secure=false、SameSite=Lax(localhost では HTTP を許可) - Test:
secure=false、SameSite=Strict(CSRF 対策をより厳格に) - Production:
secure=true、SameSite=Strict(HTTPS 必須)
- リフレッシュトークン用の HttpOnly Cookie
- sessionStorage に保存するアクセストークン
- ログアウト時の Redis ベーストークンブラックリスト
- 環境別の Cookie 属性(secure / SameSite)
- 環境変数による CORS 設定
- 環境変数による必須化された
JWT_SECRET
- リフレッシュごとのトークンローテーション(現在:リフレッシュトークンは 7 日間有効)
- 追加保護のための CSRF トークン統合
- 認証エンドポイントへのレートリミット
- CSRF:ステートレスな JWT フローのため無効化。本番では
SameSite=Strictで緩和。より高いセキュリティ要件では CSRF トークンの追加を検討。 - トークンローテーション:現在、リフレッシュトークンは 7 日間有効。使用ごとにローテーションすると盗難検知が可能になるが、複雑度は上がる。
- ステートレス性:JWT はステートレスだが、トークン失効には Redis の状態が必要。
- セッション結合:リフレッシュトークンは元のデバイス / セッションに紐付けられる。
Maven を使って結合テストを実行します:
./mvnw testテストは分離のため H2 データベースと組み込み Redis 設定を使用します。
MIT