5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【認証】JWT認証とは

Last updated at Posted at 2025-09-02

1.JWT(JSON Web Tokens)とは

JWT認証は、Webアプリケーションでユーザーの認証状態を管理する仕組みの一つです。
従来のセッションベース認証とは異なり、
サーバー側で状態を保持せずに認証を行うことができるため、
現代のWebアプリケーションで広く使われています。

2.JWTの基本構造

JWT は「ドット(.)」で区切られた 3つの部分 から成り立っています。
ヘッダー.ペイロード.署名

■Header(ヘッダー)
トークンのタイプ(JWT)と使用する暗号化アルゴリズムを指定します。
Base64でエンコードされています。

{
  "alg": "HS256",
  "typ": "JWT"
}

■Payload(ペイロード)
実際のデータ(クレーム)が含まれる部分です。
ユーザーID、権限、有効期限などの情報が格納されます。
こちらもBase64でエンコードされています。

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622
}

■Signature(署名)
Header、Payload、および秘密鍵を使って生成される署名です。
トークンが改ざんされていないことを検証するために使用されます。

署名部分は.envなどで外部に漏れないように管理することが多いです。

.env
JWT_SECRET=your-super-secret-key-here-should-be-very-long-and-random

■Base64エンコード後のJWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

3.JWT認証の流れ

ステップ1: ログイン
ユーザーがユーザー名とパスワードでログインします。

ステップ2: トークン生成
サーバーが認証情報を確認し、有効であればJWTトークンを生成して返します。

ステップ3: トークン保存
クライアントは受け取ったJWTトークンを保存します(通常はlocalStorageやcookieに)。

ステップ4: API呼び出し
以降のAPI呼び出しでは、HTTPヘッダーにJWTトークンを含めて送信します。

ステップ5: トークン検証
サーバーはトークンの有効性を検証し、有効であればリクエストを処理します。

4.実装例

バックエンド(Node.js+Express)

// server.js
const express = require("express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const cors = require("cors");

const app = express();
app.use(express.json());
app.use(cors()); // Next.jsとの通信を許可

// JWT用の秘密鍵(本番では環境変数で管理!)
const JWT_SECRET = process.env.JWT_SECRET || (() => {
  throw new Error('JWT_SECRET環境変数が設定されていません');
})();

// ダミーユーザー(本来はDBから取得)
const user = {
  id: 1,
  username: "testuser",
  // "password123" をハッシュ化したもの
  password: bcrypt.hashSync("password123", 10),
};

// ✅ ログインAPI(JWT発行)
app.post("/login", (req, res) => {
  const { username, password } = req.body;

  if (username !== user.username) {
    return res.status(401).json({ message: "ユーザーが存在しません" });
  }

  // パスワード検証
  const isPasswordValid = bcrypt.compareSync(password, user.password);
  if (!isPasswordValid) {
    return res.status(401).json({ message: "パスワードが間違っています" });
  }

  // JWT作成(有効期限1時間)
  const token = jwt.sign({ id: user.id }, JWT_SECRET, {
    expiresIn: "1h",
  });

  res.json({ token });
});

// ✅ 認証ミドルウェア
function authenticateToken(req, res, next) {
  const authHeader = req.headers["authorization"];
  const token = authHeader && authHeader.split(" ")[1];

  if (!token) return res.sendStatus(401);

  // ログイン時に取得したJWTトークンで認証を行う
  jwt.verify(token, JWT_SECRET, (err, decoded) => {
    if (err) return res.sendStatus(403);
    req.user = decoded; // デコードされたユーザー情報を保存
    next();
  });
}

// ✅ 保護されたルート
app.get("/profile", authenticateToken, (req, res) => {
  res.json({
    message: "認証OK!プロフィール情報を返します。",
    user: req.user,
  });
});

app.listen(4000, () => {
  console.log("Server running on http://localhost:4000");
});

フロントエンド(Next.js)

// app/page.tsx
"use client";

import { useState } from "react";

export default function HomePage() {
  const [username, setUsername] = useState("testuser");
  const [password, setPassword] = useState("password123");
  const [token, setToken] = useState("");
  const [profile, setProfile] = useState<any>(null);

  // 🔑 ログインしてJWTトークン取得
  const handleLogin = async () => {
    const res = await fetch("http://localhost:4000/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username, password }),
    });

    if (res.ok) {
      const data = await res.json();  // サーバーから返されたJWTトークンを設定
      setToken(data.token);
      localStorage.setItem("jwt", data.token); // 簡易サンプルなのでlocalStorage保存
      alert("ログイン成功!トークンを保存しました。");
    } else {
      alert("ログイン失敗!");
    }
  };

  // 🔒 プロフィール取得(ログイン時に取得したJWTトークンが必要)
  const handleGetProfile = async () => {
    const savedToken = localStorage.getItem("jwt");

    // サーバー接続時、HeaderにBearer認証を設定する際、JWTトークンを設定して送信する
    const res = await fetch("http://localhost:4000/profile", {
      method: "GET",
      headers: {
        Authorization: `Bearer ${savedToken}`,
      },
    });

    if (res.ok) {
      const data = await res.json();
      setProfile(data);
    } else {
      alert("認証エラー!");
    }
  };

  return (
    <div className="p-6 space-y-4">
      <h1 className="text-2xl font-bold">JWT 認証サンプル</h1>

      {/* ログインフォーム */}
      <div className="space-y-2">
        <input
          className="border p-2"
          placeholder="ユーザー名"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
        <input
          className="border p-2"
          placeholder="パスワード"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
        <button className="bg-blue-500 text-white px-4 py-2 rounded" onClick={handleLogin}>
          ログイン
        </button>
      </div>

      {/* トークン表示 */}
      {token && (
        <div className="bg-gray-100 p-2 rounded">
          <p><strong>保存されたトークン:</strong></p>
          <p className="break-all text-sm">{token}</p>
        </div>
      )}

      {/* 認証付きリクエスト */}
      <div>
        <button className="bg-green-500 text-white px-4 py-2 rounded" onClick={handleGetProfile}>
          プロフィール取得
        </button>
      </div>

      {/* プロフィール表示 */}
      {profile && (
        <div className="bg-gray-100 p-4 rounded mt-4">
          <h2 className="text-xl font-semibold">プロフィール情報</h2>
          <pre className="text-sm">{JSON.stringify(profile, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}

5.まとめ

JWTとは

  • トークンのフォーマット(形式)のひとつ
  • Header.Payload.Signature という3部構成で、署名付きで改ざん防止可能
  • 誰がログインしているか、いつまで有効か、などの情報を自己完結的に所持できる

Bearer認証との関係

  • Bearer認証:HTTPヘッダーにトークンを入れて送る認証方式のひとつ
    Authorization: Bearer <アクセストークン>
  • JWT認証:Bearer認証の仕組みを使って、トークンにJWTを利用
    Authorization: Bearer <JWT>

ということを理解できました。
認証も色々あって、頭がパンパンです。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?