時系列でフロントとバックのやることは?
JWTは基本的にサーバー側で状態を持たないため、セッション管理がシンプルになり、負荷分散がしやすいメリットがあげられます。
ただし、セキュリティの観点からHTTPSで通信し、必要に応じてトークンのリフレッシュや失効管理を行うことが推奨されるらしいです。
1. ユーザーがログイン情報を入力する
フロントエンド: ユーザーがメールアドレスやパスワードなどの認証情報をログインフォームに入力し、サインインリクエストを送信します。
2. 認証リクエストをバックエンドに送信する
フロントエンド: 入力された情報をバックエンドに送信します。通常、HTTPSを使ってセキュアにAPIリクエストを行います。
バックエンド: リクエストを受け取り、送信された認証情報(メールアドレス、パスワードなど)を検証します。
3. ユーザー情報を認証する
バックエンド: データベースから該当するユーザー情報を検索し、パスワードなどを照合します。正しい情報であれば、JWTを生成します。
4. JWTを生成して返す
バックエンド: ユーザーが認証された場合、JWTを生成します。JWTにはユーザーID、ロール(役割)、有効期限などの情報を含め、秘密鍵で署名を行います。
フロントエンド: バックエンドから返されたJWTを受け取り、一般的にはブラウザのローカルストレージまたはセッションストレージに保存します。
5. 以降のリクエストにJWTを添付する
フロントエンド: 認証が必要なAPIリクエストを送る際に、リクエストヘッダーのAuthorizationフィールドにBearer 形式でトークンを添付します。
6. バックエンドがJWTを検証する
バックエンド: リクエストを受け取った際にJWTが添付されているか確認し、JWTの署名と有効期限を検証します。問題がなければリクエストを認証済みとして処理します。
7. リソースまたはデータを返す
バックエンド: JWTが有効であれば、ユーザーリクエストに応じたデータやリソースを返します。
フロントエンド: 返されたデータを表示します。
8. トークンの更新または期限切れ
フロントエンド: JWTの有効期限が近づくと、リフレッシュトークンがあればそれを使ってトークンの更新をリクエストします。
バックエンド: リフレッシュトークンを確認し、新しいJWTを発行してフロントエンドに返します。
9. ログアウト処理
フロントエンド: ユーザーがログアウト操作を行うと、ストレージからJWTを削除します。
バックエンド: (必要に応じて)JWTを無効化するための処理を行います(例: サーバー側でブラックリストに登録)。
JavaScriptでの実装イメージ
フロント(JavaScript)
ログイン時にJWTを取得して保存する
async function login(email, password) {
try {
const response = await fetch('https://yourapi.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (response.ok && data.token) {
// トークンをローカルストレージに保存
localStorage.setItem('jwt', data.token);
alert("ログイン成功!");
} else {
alert("ログイン失敗");
}
} catch (error) {
console.error("Error:", error);
}
}
JWTを使ったAPIリクエスト
async function getProtectedData() {
const token = localStorage.getItem('jwt');
if (!token) {
alert("ログインが必要です");
return;
}
try {
const response = await fetch('https://yourapi.com/protected', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const data = await response.json();
console.log("Protected Data:", data);
} else {
alert("認証エラー");
}
} catch (error) {
console.error("Error:", error);
}
}
ログアウト処理
function logout() {
localStorage.removeItem('jwt'); // トークンを削除
alert("ログアウトしました");
}
バック(Node.js + Express)
JWTの生成と返却
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
// 秘密鍵(実際の運用では環境変数などで設定)
const SECRET_KEY = 'your_secret_key';
// ログインエンドポイント
app.post('/login', (req, res) => {
const { email, password } = req.body;
// ここでユーザー認証(例: データベース照合など)
if (email === "test@example.com" && password === "password") {
// JWTを生成
const token = jwt.sign({ email }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).json({ message: '認証失敗' });
}
});
JWTを検証するミドルウェア
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
リクエストが来るとき、JWTを検証するミドルウェアを挟んでJWTをチェック
app.get('/protected', authenticateToken, (req, res) => {
res.json({ message: '認証済みのデータにアクセスしました!', user: req.user });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
参考サイト