1
0

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 〜JWTって結局何?〜

1
Posted at

導入

たきの「博士〜!」

たきの「遊園地に来たんですが、この紙を見せるだけでアトラクションに乗れるんですよ!」

博士「ほう、それは入園パスじゃな。」

たきの「一回チケット売り場で本人確認をしただけなのに、その後は何回見せても大丈夫なんですね。」

博士「ふむ。」

博士「実はWebサービスにも、それとよく似た仕組みがあるんじゃ。」

たきの「Webサイトにも入園パスがあるんですか?」

博士「それが**JWT(JSON Web Token)**じゃ。」

JWT

博士「JWT(JSON Web Token)とは、ログインしたユーザーであることを証明するためのトークンじゃ。」

たきの「トークン?」

博士「簡単に言えばデジタル会員証のようなものじゃ。」

例えば、一度ログインすると、

ユーザー
   │
   │ ログイン
   ▼
サーバー
   │
   │ JWTを発行
   ▼
eyJhbGciOiJIUzI1NiIs...

その後は、

GET /mypage

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

のようにJWTを送るだけで、

「この人はログイン済みだ」

と判断できる。

たきの「なるほど!」

博士「さらにJWTには、認証だけでなく、認可に必要な情報も入れられるんじゃ。」

たきの「博士..。認証、認可ってなんでしたっけ?」

認証

博士「認証とは、あなたは誰ですか? を確認することじゃ。」

例えば、

ID:takino
Password:1234

を入力すると、

サーバー

「あ、この人はたきのさんだ」

と本人確認を行う。

つまり、

認証 = 本人確認

である。

たきの「遊園地でいうと、チケット売り場で本人確認している場面なんですね!」

博士「その通り。」

博士「そして本人確認が終わると、JWTという入園パスが発行される。」

たきの「なるほど!」

たきの「じゃあJWTを持っていたら、どこへでも入れるんですか?」

博士「いや、それは違う。」

博士「そこで出てくるのが認可じゃ。」

認可

博士「認可とは、その人が何をしてよいか決めることじゃ。」

例えば、

一般ユーザー

なら

  • 商品を見る
  • 商品を買う

だけできる。

一方、

管理者

なら

  • 商品追加
  • 商品削除
  • ユーザー削除

などもできる。

つまり、

認証
↓
本人確認

認可
↓
権限確認

という違いになる。

たきの「なるほど!」

たきの「会員証を持っていても、入れない部屋があるような感じですね!」

博士「その通りじゃ。」

博士「そして、その権限もJWTに入れられるんじゃ。」

JWTでの認証・認可

博士「例えばJWTにはこんな情報が入っておる。」

{
  "sub": "takino",
  "role": "ADMIN"
}

サーバーは、

role = ADMIN

を見て、

管理画面OK

と判断する。

逆に、

role = USER

なら、

403 Forbidden

となる。

博士「つまりJWTには、

  • この人は誰なのか(認証)
  • この人は何ができるのか(認可)

の情報が入っているんじゃ。」

たきの「なるほど!」

たきの「JWTは**eyJhbGciOiJIUzI1NiIs...**みたいな形でしたよね」

たきの「中身ってどうなっているんですか?」

JWTの中身

JWTは3つの部分からできている。

xxxxx.yyyyy.zzzzz
部分 内容
Header 使用する署名アルゴリズム
Payload ユーザー情報・権限・有効期限
Signature 改ざん防止の署名

実際のJWTは次のような形じゃ。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiJ0YWtpbm8iLCJyb2xlIjoiQURNSU4iLCJleHAiOjE3NTAwMDAwMDB9
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

例えばPayloadには、

{
  "sub":"takino",
  "role":"ADMIN",
  "exp":1750000000
}

のような情報が入る。

Payloadは暗号化ではなく、Base64でエンコードされているだけです。

パスワードなどの機密情報は保存してはいけません。

たきの「なるほど!」

Session認証との比較

博士「JWTと一番比較されるのがSession認証じゃ。」

項目 Session認証 JWT
ログイン情報の保存場所 サーバー クライアント
通信時に送るもの Session ID JWT
サーバー側の管理 セッションを管理する必要がある セッション管理が不要
サーバーを増やした場合 セッション共有が必要 トークンを検証するだけでよい
向いている場面 従来のWebサイト Web API・SPA・スマホアプリ

博士「ReactやFlutterでは、サーバー側でSessionを管理しなくてよいJWTがよく使われる。」

たきの「なるほど!」

たきの「JWT,session以外にも認証方法ってあるんですか?」

博士「もちろんじゃ。」

他にもよく使われる認証・認可との比較

方式 何をする仕組み?
Session認証 サーバーでログイン状態を管理する 一般的なWebサイト
JWT トークンを使ってログイン状態をやり取りする Web API
OAuth 2.0 他サービスに権限を渡す Google Drive連携
OpenID Connect 他サービスでログインする Googleログイン

たきの「認証と認可、それにJWTとの違いも分かってきました!」

博士「ふむ。」

博士「では、JWT認証が実際にどのように動いているのか見ていこう。」

JWT認証の流れ

まずはログインする。

クライアント
(ブラウザ・アプリ)
        │
ID・Password
        ▼
サーバー
        │
認証
        │
JWTを発行
        ▼
クライアント

このとき、

  1. ユーザーがIDとパスワードを入力する
  2. サーバーが本人確認(認証)を行う
  3. 認証に成功するとJWTを発行する
  4. クライアントがJWTを保存する

たきの「ここまでは普通のログインと同じなんですね。」

博士「そうじゃ。」

博士「違うのは、この後じゃ。」

APIへアクセスするときは、

クライアント
        │
Authorization: Bearer JWT
        ▼
サーバー
        │
JWTを検証
        │
ログイン済みと判断
        ▼
API実行

つまり、

  1. クライアントはJWTを付けてAPIへアクセスする
  2. サーバーはJWTが正しいか確認する
  3. 問題がなければログイン済みとしてAPIを実行する

博士「最初だけIDとパスワードを送り、その後はJWTだけを送るんじゃ。」

たきの「だから毎回ログインし直さなくてもいいんですね!」

博士「その通り。」

博士「では実際のコードを見てみよう。」

Spring Bootでの実装例

博士「JWT認証をSpring Bootで実装する流れを見てみよう。」

① ログインAPI

まずはログインAPIを作成する。

ユーザーが入力したID・パスワードを確認し、正しければJWTを発行する。

@PostMapping("/login")
public String login(@RequestBody LoginRequest request) {

    // ユーザー名・パスワードが正しいか確認(認証)
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(
            request.getUsername(),
            request.getPassword()
        )
    );

    // 認証成功後、JWTを発行
    return jwtService.generateToken(request.getUsername());
}

ポイント

  • authenticate()で本人確認(認証)を行う
  • 認証に成功するとJWTを発行する
  • JWTをクライアントへ返す

② JWTを生成

JWTにはユーザー情報や有効期限などを設定する。

public String generateToken(String username) {

    return Jwts.builder()

            // ユーザー名を保存
            .subject(username)

            // 発行日時
            .issuedAt(new Date())

            // 有効期限(1時間)
            .expiration(new Date(System.currentTimeMillis() + 3600000))

            // 改ざん防止の署名
            .signWith(secretKey)

            // JWTを生成
            .compact();
}

ポイント

このJWTには、

  • ユーザー名
  • 発行日時
  • 有効期限
  • 署名

が保存される。

③ JWTを送信

ログイン後は、APIへアクセスするたびにJWTをHTTPヘッダーへ付けて送信する。

fetch("/api/users", {
  headers: {

    // JWTを付与
    Authorization: `Bearer ${token}`

  }
});

実際にはこのようなHTTPリクエストになる。

GET /api/users

Authorization: Bearer eyJhbGc...

ポイント

サーバーは、このJWTを見てログイン済みか判断する。

④ JWTを取得

サーバーは、送られてきたJWTをAuthorizationヘッダーから取り出す。

// Authorizationヘッダーを取得
String authHeader = request.getHeader("Authorization");

// Bearerから始まる場合だけ取得
if (authHeader != null &&
    authHeader.startsWith("Bearer ")) {

    // JWTだけ取り出す
    String jwt = authHeader.substring(7);
}

ポイント

Bearer eyJhbGc...
       ↑
      JWT

Bearerを取り除いた文字列がJWTである。

⑤ JWTを検証

最後に、JWTが改ざんされていないか、有効期限が切れていないかを確認する。

// JWTからユーザー名を取得
String username = jwtService.extractUsername(jwt);

// 改ざん・期限切れを確認
if (jwtService.isTokenValid(jwt)) {

    // Spring Securityへログイン済みとして登録
    UsernamePasswordAuthenticationToken auth =
            new UsernamePasswordAuthenticationToken(
                    username,
                    null,
                    List.of());

    SecurityContextHolder.getContext()
            .setAuthentication(auth);
}

ポイント

JWTが正しいと判断されると、

  • ログイン済み
  • ユーザー情報を取得できる
  • 認可(権限チェック)ができる

ようになる。

処理の流れ

API受信
   │
Authorizationヘッダー取得
   │
JWTを取り出す
   │
JWTを検証
   │
ログイン済みと判断
   │
API実行

まとめ

博士「JWTを一言で表すと…」

ログイン済みを証明するデジタル会員証

JWTを利用すると、

  • サーバー側でSession管理が不要
  • ReactやFlutterとの相性が良い
  • REST APIで広く利用されている
  • 認証・認可の情報をトークンでやり取りできる

というメリットがある。


たきの「遊園地の入園パスで考えると、JWTが何をしているのかイメージしやすかったです!」

博士「ふむ。」

博士「では、今日の一句」

今日の一句

トークンを 取っとくん?と 問うトークン

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?