🙄
# 4.2 JWT 認証の仕組みと実装(Django REST Framework + Vue 編)
前回の記事ではセッション認証を使ったログインセッション管理を紹介した。
今回は外部公開 API や EC サイトでよく使われる JWT 認証 について整理する。
1. JWT とは何か
JWT(JSON Web Token)は、認証情報を JSON 形式でエンコードしたトークン。
以下の3部構成になっている。
- Header: アルゴリズムやタイプ
- Payload: ユーザー ID や有効期限
- Signature: 秘密鍵で署名
例:
xxxxx.yyyyy.zzzzz
特徴
- サーバー側にセッションを保持しない → stateless 認証
- トークンそのものにユーザー情報が含まれる
- 有効期限を超えると無効になる
2. Django REST Framework での JWT 実装
DRF では djangorestframework-simplejwt を使うのが一般的。
インストール
pip install djangorestframework-simplejwt
設定
# settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
)
}
URL ルーティング
# urls.py
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]
レスポンス例(ログイン成功時)
{
"access": "jwt_access_token_string",
"refresh": "jwt_refresh_token_string"
}
- access: 短期トークン(API 呼び出し用)
- refresh: 長期トークン(access の再発行用)
3. Vue 側での利用方法
ログイン処理
import axios from "axios";
async function login(username, password) {
const res = await axios.post("/api/token/", { username, password });
localStorage.setItem("access_token", res.data.access);
localStorage.setItem("refresh_token", res.data.refresh);
}
API リクエスト時のヘッダー付与
api.interceptors.request.use(config => {
const token = localStorage.getItem("access_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
トークンのリフレッシュ
api.interceptors.response.use(
response => response,
async error => {
if (error.response.status === 401) {
const refresh = localStorage.getItem("refresh_token");
if (refresh) {
const res = await axios.post("/api/token/refresh/", { refresh });
localStorage.setItem("access_token", res.data.access);
error.config.headers.Authorization = `Bearer ${res.data.access}`;
return api.request(error.config);
}
}
return Promise.reject(error);
}
);
4. 実運用でハマるポイント:リフレッシュ競合問題
現象
- 1画面で複数の API を同時に呼び出す
- access トークンの期限が切れていると 同時に 401 エラーが返る
- それぞれのリクエストが 個別に refresh API を叩いてしまう
- refresh トークンは「一度使ったら無効」になる設定が多いため、競合で失敗する
結果 → 画面がエラーになり、ユーザーがログアウト状態になる
解決策 1: フロント側でリフレッシュ処理を直列化
リフレッシュ処理を「同時に 1回だけ」実行し、他のリクエストは待機させる。
👉 実装例は割愛(前章のサンプル参照)
解決策 2: refresh トークンを複数回使える設定にする
djangorestframework-simplejwt では以下を設定できる:
SIMPLE_JWT = {
"ROTATE_REFRESH_TOKENS": False,
}
これにより同じ refresh トークンを複数回使えるため、並列リクエストでも失敗しない。
ただし「盗まれた refresh が無効化されにくい」ためセキュリティリスクが高まる。
5. セキュリティの考慮点
-
保存場所
-
localStorage: XSS に弱い - Cookie +
HttpOnly: セキュリティは高いが CSRF 対策が必要
-
-
有効期限設計
- access: 短め(数分〜数十分)
- refresh: 長め(数日〜数週間)
-
ログアウト処理
- refresh をブラックリスト化できるようにする
6. AI活用のポイント
実際の開発では、JWT 認証まわりは 「一見動くけど細部で不安定」 になりがち。
- Vue 側で API をコールするタイミング(ページロード直後・ルーティング遷移後・F5 リロード後)によって挙動が変わる
- リフレッシュ処理やログイン判定が「リンク遷移だと効かない」「F5押すと 401 で落ちる」といったケースが発生する
- このあたりは AI が生成したコードをベースにすれば一度は通るが、実際のフローで微妙な動きを調整する必要がある
👉 AI の使い方としては、
- まず AI にインターセプターやリフレッシュ処理の雛形を出させる
- それをローカルで試し、F5 や別リンク遷移など「実利用の動線」でテストする
- 挙動が不安定なら、ログを出しながら逐次修正
この反復で仕上げるのが現実的。
AI のコード生成は「初期セットアップの高速化」には最適だが、最終的な安定動作は人間の検証が必須になる。
7. セッション認証と JWT 認証の使い分け
| 項目 | セッション認証 | JWT 認証 |
|---|---|---|
| 管理方式 | サーバーがセッションを保持 | stateless(トークンのみ) |
| CSRF 対策 | 必要 | 基本不要 |
| 有効期限 | セッションタイムアウト | トークンの exp |
| スケーラビリティ | セッション共有が必要 | スケールアウトしやすい |
| 向いているケース | 社内業務システム | EC サイト、スマホアプリ、外部 API |
まとめ
- JWT は stateless な認証方式 で外部公開 API に向いている
-
djangorestframework-simplejwtで簡単に導入可能 - Vue 側では access/refresh を組み合わせて扱う
- 複数 API を同時に叩いたときのリフレッシュ競合問題 に注意
- 実運用では リロードや遷移時の微妙な挙動 が起きやすく、AI のコードは叩き台にして「人間の検証で仕上げる」ことが重要
- プロジェクトでは 業務システムはセッション認証、EC サイトは JWT 認証 と使い分けている
次回は、部署・権限ごとのメニュー制御 を取り上げる予定。
Discussion