735
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

【JWT】 入門

JWTとは

  • 公式サイト
  • JSON Web Tokenの略  
  • 電子署名により、改ざん検知できる。
  • 認証用のトークンなどで用いられる。

構成

  • ヘッダペイロード署名の3つから成る。
  • それぞれは、Base64でエンコードされている
  • それぞれは、 . (ドット) で結合されている。
JWTの構文
[ヘッダ].
[ペイロード].
[署名]
実際のJWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs

作り方

 1. アルゴリズムTokenのタイプを、ヘッダに設定

アルゴリズム 説明
HMAC SHA-256 256ビットのハッシュ値を生成する関数
ヘッダ
{
  "alg": "HS256",
  "typ": "JWT"
}

 2. ヘッダをBase64でエンコード

ヘッダをBase64でエンコード
echo -n '{"alg":"HS256","typ":"JWT"}' | base64
Base64エンコードされたヘッダ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

 3. ペイロードを設定

クレーム名 説明
issued at JWT を発行した時刻
subject JWT の主語となる主体の識別子
ペイロード
{
  "sub": "1234567890",
  "iat": 1516239022
}

 4. ペイロードをBase64でエンコード

ペイロードをBase64でエンコード
echo -n '{"sub":"1234567890","iat":1516239022}' | base64
Base64エンコードされたヘッダ
eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ

 5. ヘッダとペイロードを . (ドット) で結合し、署名なしTokenを生成

署名なしトークン
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ

 6. 署名なしTokenに対し、秘密鍵とHMAC-SHA256を用いて署名を生成

署名なしTokenに、HMAC-SHA256を適応
echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ' | \
openssl dgst -binary -sha256 -hmac 'secret' | \
base64
署名
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs

 7. 署名なしTokenと署名を . (ドット) で結合

JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs

実装

JWTの生成と検証を、Javascriptで実装する。

前提条件

秘密鍵の値を、secretとする

JWTの生成

JWTの生成
const crypto = require('crypto')

const base64 = json => {
    const jsonStr = JSON.stringify(json)
    const jsonB64 = Buffer.from(jsonStr).toString('base64')
    const jsonB64NoPadding = jsonB64.replace(/={1,2}$/, '')
    return jsonB64NoPadding
}

const HMAC_SHA256 = (key, data) => {
    const hash = crypto.createHmac('sha256', key).update(data).digest('base64')
    const hashNoPadding = hash.replace(/={1,2}$/, '')
    return hashNoPadding
}

const header = { alg: 'HS256', typ: 'JWT' }
const payload = { sub: '1234567890', iat:1516239022 }
const key = 'secret'
const unsignedToken = `${base64(header)}.${base64(payload)}`
const signature = HMAC_SHA256(key, unsignedToken)
const jwt = `${unsignedToken}.${signature}`

console.log(jwt)
出力結果
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs
  • JSONをBase64エンコーディング
    alt

  • HMAC-SHA256より、JWTを生成

alt

JWTの検証

JWTの検証
const crypto = require('crypto')

const HMAC_SHA256 = (key, data) => {
    const hash = crypto.createHmac('sha256', key).update(data).digest('base64')
    const hashNoPadding = hash.replace(/={1,2}$/, '')
    return hashNoPadding
}

const key = 'secret'
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.t42p4AHef69Tyyi88U6+p0utZYYrg7mmCGhoAd7Zffs'

const splits = jwt.split('.')
const unsignedToken = [splits[0], splits[1]].join('.')
const signature = splits[2]

console.log(HMAC_SHA256(key, unsignedToken) === signature)
出力結果
true

alt

認証におけるJWTの利用

新規登録/ログインより、ユーザはJWTを取得する。
APIを利用する際は、JWTを付与してリクエストを行う。
例として、JWTを利用し、ユーザ照会APIにリクエストを行う。

前提条件

  • ペイロードは、ユーザIDと有効期限の情報をもつ
ペイロード
{
  "user_id": "1234567890",
  "iat": 1516239022
}
  • APサーバは、以下のAPIを提供している。
Method URL Parameter Response 説明
GET /user Authorization ユーザ名を返す ユーザ情報を返す
Method URL Body Response 説明
POST /register id, password Tokenを返す ユーザ登録を行う
POST /login id, password Tokenを返す ログインを行う

新規登録する場合

  1. ユーザ登録
    name=hoge, password=hogeとし、 /registerにPOSTリクエストする。
    ② APサーバは、DBに対し、新規登録をリクエストする。
    ③ DBは、ユニークなIDを設定しつつ、name, passwordの登録を行う。
    ④ DBは、APサーバに対し、ユーザIDを返す。
    ⑤ APサーバは、ユーザIDと秘密鍵より、Tokenを生成する。
    ⑥ APサーバは、ユーザにTokenを返す。
    alt

  2. ユーザ情報をリクエスト
    AuthorizationにJWTを設定し、 /userにGETリクエストする。
    ② APサーバは、秘密鍵を用いてTokenを検証する。
    ③ APサーバは、TokenからユーザIDを取得する。
    ④ APサーバは、DBに対し、IDに対するユーザ情報をリクエストする。
    ⑤ DBは、IDに対するユーザ情報を返す。
    ⑥ APサーバは、ユーザにユーザ情報を返す。
    alt

ログインする場合

  1. ログイン
    name=hoge, password=hogeとし、 /loginにPOSTリクエストする。
    ② APサーバは、DBに対し、ユーザの確認をリクエストする。
    ④ DBは、APサーバに対し、対応するユーザIDを返す。
    ⑤ APサーバは、ユーザIDと秘密鍵より、Tokenを生成する。
    ⑥ APサーバは、ユーザにTokenを返す。
    alt

  2. ユーザ情報をリクエスト
    AuthorizationにJWTを設定し、 /userにGETリクエストする。
    ② APサーバは、秘密鍵を用いてTokenを検証する。
    ③ APサーバは、TokenからユーザIDを取得する。
    ④ APサーバは、DBに対し、IDに対するユーザ情報をリクエストする。
    ⑤ DBは、IDに対するユーザ情報を返す。
    ⑥ APサーバは、ユーザにユーザ情報を返す。
    alt

まとめ

JWTを実装しました。実装することで、理解が深まると思います。
また、認証におけるJWTの利用方法についてまとめました。
間違い・指摘等があればコメントお願いします。

参考文献

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
735
Help us understand the problem. What are the problem?