<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jwt Token Validation on K-Life Hack | システムアーキテクチャ &amp; DevOps</title><link>https://klifehack.com/tags/jwt-token-validation/</link><description>Recent content in Jwt Token Validation on K-Life Hack | システムアーキテクチャ &amp; DevOps</description><generator>Hugo -- gohugo.io</generator><language>ja</language><lastBuildDate>Sat, 01 Aug 2026 10:09:03 +0900</lastBuildDate><atom:link href="https://klifehack.com/tags/jwt-token-validation/index.xml" rel="self" type="application/rss+xml"/><item><title>大規模アクセスに耐えるチケット予約バックエンドの設計とRedis・MySQL連携パターン</title><link>https://klifehack.com/p/snaptix-high-concurrency-ticketing-backend/</link><pubDate>Sat, 01 Aug 2026 10:09:03 +0900</pubDate><guid>https://klifehack.com/p/snaptix-high-concurrency-ticketing-backend/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;先着順チケット販売プラットフォームにおけるRedisとSpring Securityを活用した高トラフィック制御アーキテクチャ&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;meta_description&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;アクセススパイク時におけるデータベース保護、Redisアトミック操作による在庫制御、Spring Securityの循環参照回避、および障害隔離設計の技術レポート。&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#34;先着順のチケット販売プラットフォーム構築において、アクセススパイク時のデータベース保護と整合性の維持は極めて重要な課題です。従来のペシミスティックロック（`SELECT&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;...&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;FOR&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;UPDATE`）に依存する設計では、短時間に集中するリクエストによってMySQLの接続プールが枯渇し、クエリレイテンシの増大やデッドロックを誘発するリスクが高まります。このような制約に対応するため、ホットパストラザクションからデータベースの排他ロックを分離し、インメモリ処理と非同期制御を組み合わせたアーキテクチャへの移行が必要となります。\n\n##&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;認証サブシステムの設計と循環参照の回避\n\n認証層には、ステートレスなJWT（JSON&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;Web&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;Token）とセキュアなHTTP&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;Cookieを採用しています。AccessTokenおよびRefreshTokenはリクエストヘッダーで直接扱わず、`HttpOnly`、`Secure`、`SameSite=Strict`&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;フラグを付与したCookie経由で伝達することでセキュリティを担保します。\n\nSpring&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;Security構成において、セキュリティ設定クラスと暗号化コンポーネント間の循環依存（Circular&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;Dependency）を防ぐため、`PasswordEncoder`&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;ビーンを独立した設定クラスへ切り出しています。\n\n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;```kotlin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;class&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;BeanConfig&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@Bean&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;fun&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;passwordEncoder():&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;PasswordEncoder&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;return&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;BCryptPasswordEncoder()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;\n\nHTTPリクエスト処理時、&lt;code&gt;JwtAuthenticationFilter&lt;/code&gt; は &lt;code&gt;UsernamePasswordAuthenticationFilter&lt;/code&gt; の前に実行され、Cookieからトークンを抽出・検証して &lt;code&gt;SecurityContextHolder&lt;/code&gt; に &lt;code&gt;AuthenticatedUser&lt;/code&gt; プリンシパルを設定します。認証エラー（401 Unauthorized）および認可エラー（403 Forbidden）の捕捉は、カスタムの &lt;code&gt;CustomAuthenticationEntryPoint&lt;/code&gt; と &lt;code&gt;CustomAccessDeniedHandler&lt;/code&gt; によって統一されたJSON形式でクライアントに返却されます。\n\n## イベント在庫制御とRedis MGETによるバッチ参照\n\nイベント販売時における在庫減算処理は、MySQLへの直接更新を避け、Redis上のアトミックなデータ操作（Luaスクリプトおよび &lt;code&gt;DECRBY&lt;/code&gt; 命令）によって行われます。イベントおよびエリアごとの在庫データは以下のキー形式でRedis内に保持されます。\n\n- イベントメタデータキャッシュ: &lt;code&gt;event:info:{eventPublicId}&lt;/code&gt; (TTL: 1時間)\n- エリア別リアルタイム在庫: &lt;code&gt;ZONE:{zoneInternalId}:stock&lt;/code&gt; (Integer String)\n- 注文要求ストリームキュー: &lt;code&gt;queue:order:{eventId}&lt;/code&gt; (Redis Stream)\n\n大量のエリア在庫情報を取得する際、個別キーに対する単一読み込みはネットワーク往復オーバーヘッド（RTT）を増大させます。これを回避するため、&lt;code&gt;MGET&lt;/code&gt; を活用したパイプライン化参照を実装しています。\n\n&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Component&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;StockRedisGateway&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; redisTemplate: StringRedisTemplate
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getAll&lt;/span&gt;(zoneIds: List&amp;lt;long&amp;gt;): Map&amp;lt;long, long?=&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (zoneIds.isEmpty()) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; emptyMap()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; keys = zoneIds.map { &lt;span style="color:#e6db74"&gt;&amp;#34;ZONE:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;$it&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:stock&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; values = redisTemplate.opsForValue().multiGet(keys) &lt;span style="color:#f92672"&gt;?:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; emptyMap()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; zoneIds.zip(values).toMap().mapValues { entry -&amp;amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;entry.&lt;span style="color:#66d9ef"&gt;value&lt;/span&gt;&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;toLongOrNull()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;\n\nRedisノードの障害時やネットワーク断絶時には、即座に例外を捕捉し、データベース上の確定済み予約数から残数を算出する動的フォールバック（&lt;code&gt;total_capacity - confirmed_count&lt;/code&gt;）へ切り替える設計としています。\n\n## 非同期アラート機構と障害隔離\n\nインフラ障害やデータ不整合を検知するアラートシステムは、メインのビジネスロジック（注文・決済処理）をブロックしないよう非同期スレッドプール（&lt;code&gt;alertExecutor&lt;/code&gt;）で隔離して実行されます。また、短期間に大量の同一通知がスラックへ送信される事態を防ぐため、インメモリの &lt;code&gt;ConcurrentHashMap&lt;/code&gt; を用いたローカルスロットリング制御（&lt;code&gt;AlertThrottler&lt;/code&gt;）を導入しています。\n\nスロットリング判定にRedisを利用しない理由は、Redis自体が障害によってダウンしている局面でもアラート機能が正常に自己完結して動作する必要があるためです。\n\n## Troubleshooting\n\n### 1. Spring Security構成における BeanCurrentlyInCreationException\n- &lt;b&gt;現象&lt;/b&gt;: &lt;code&gt;SecurityConfig&lt;/code&gt; 内に &lt;code&gt;BCryptPasswordEncoder&lt;/code&gt; を直接 &lt;code&gt;@Bean&lt;/code&gt; 定義し、それを &lt;code&gt;UserDetailsService&lt;/code&gt; や &lt;code&gt;AuthService&lt;/code&gt; に注入した際、コンテナ初期化時に循環依存エラーが発生しアプリケーションが起動に失敗する。\n- &lt;b&gt;原因&lt;/b&gt;: セキュリティフィルターチェーンの組み立てフェーズと、認証処理で利用するサービス層のビーン生成フェーズが相互に参照し合っていたため。\n- &lt;b&gt;対策&lt;/b&gt;: &lt;code&gt;BeanConfig&lt;/code&gt; という独立した設定クラスを新規に作成し、&lt;code&gt;PasswordEncoder&lt;/code&gt; の定義を分離抽出することで依存関係のサイクルを遮断。\n\n### 2. Exposed ORMの batchInsert における低パフォーマンスと単一実行\n- &lt;b&gt;現象&lt;/b&gt;: 管理者によるイベントおよび複数ゾーンの括弧一括登録時、Exposedの &lt;code&gt;batchInsert&lt;/code&gt; を呼び出しているにもかかわらず、個別の &lt;code&gt;INSERT&lt;/code&gt; 文が逐次実行されレスポンスが著しく低下する。\n- &lt;b&gt;原因&lt;/b&gt;: MySQL JDBCドライバの設定においてバッチリライトが無効化されており、マルチ行挿入ステートメントへ変換されていなかったため。\n- &lt;b&gt;対策&lt;/b&gt;: JDBC接続URLのプロパティに &lt;code&gt;rewriteBatchedStatements=true&lt;/code&gt; を明示的に追加。これにより複数行の単一クエリへ変換され、一括書き込みが正常に機能することを確認。\n\n### 3. イベント終了処理（CLOSED）時のRedisキー消去漏れ\n- &lt;b&gt;現象&lt;/b&gt;: ステータス変更APIによってイベントを &lt;code&gt;CLOSED&lt;/code&gt; 状態へ切り替えた際、ネットワークの一時的な瞬断により &lt;code&gt;ZONE:{zoneId}:stock&lt;/code&gt; や &lt;code&gt;queue:order:{eventId}&lt;/code&gt; などの運用キーが消去されず残存する。\n- &lt;b&gt;原因&lt;/b&gt;: データベース状態の更新トランザクション完了後、非非同期のクリーンアップ処理中で一部の削除命令が例外により中断されていたため。\n- &lt;b&gt;対策&lt;/b&gt;: 消去処理の失敗をログに記録し、未消去キーを検出・再処理するバックグラウンドの定期実行タスクを導入して不整合を自動修復。\n\n## システム動作検証プロトコル\n\n本システムにおける認証Cookieの発行およびステータス検証、プロセス状態を確認するためのターミナル実行ログの記録例です。\n\n&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -i -X POST http://localhost:8080/api/v1/auth/login \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-H &amp;#34;Content-Type: application/json&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-d &amp;#39;{&amp;#34;email&amp;#34;:&amp;#34;admin@snaptix.internal&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;SecurePassword123!&amp;#34;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Set-Cookie: accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZSI6IlJPTEVfQURNSU4ifQ...; Path=/; Secure; HttpOnly; SameSite=Strict
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Set-Cookie: refreshToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZSI6IlJPTEVfQURNSU4ifQ...; Path=/; Secure; HttpOnly; SameSite=Strict
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Content-Type: application/json;charset=UTF-8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&amp;#34;userId&amp;#34;:1,&amp;#34;email&amp;#34;:&amp;#34;admin@snaptix.internal&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ss -tulpn | grep :8080
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tcp LISTEN 0 100 *:8080 *:* users:((&amp;#34;java&amp;#34;,pid=41029,fd=38))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ docker ps --format &amp;#34;table {{.Names}}	{{.Status}}	{{.Ports}}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAMES STATUS PORTS
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;snaptix-redis Up 2 hours 0.0.0.0:6379-&amp;amp;gt;6379/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;snaptix-mysql Up 2 hours 0.0.0.0:3306-&amp;amp;gt;3306/tcp, 33060/tcp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;\n\n## Key Takeaways\n\n1. 高コンカレンシーな在庫管理においては、データベースの行排他ロックを避け、Redisのアトミック操作とストリーム構造にトラフィックを受け止める構成が有効です。\n2. 認証機構でCookieベースのJWTを運用する際は、循環依存の回避やエラーレスポンスの統一化など、フレームワーク特有の初期化構造に留意する必要があります。\n3. キャッシュ層（Redis）の障害発生を前提とし、データベースからの動的算出へ安全にフォールバックする回路をあらかじめ組み込むことがシステムの堅牢性を左右します。&amp;quot;
}&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-&amp;lt;/long,&amp;gt;&amp;lt;/long&amp;gt;" data-lang="&amp;lt;/long,&amp;gt;&amp;lt;/long&amp;gt;"&gt;&lt;/code&gt;&lt;/pre&gt;</description></item></channel></rss>