TECH MEDIA

テックメディア


フレームワーク
ブログ

FlaskAPIでJWT認証を実装する(前編)

FlaskAPIJWT認証
目次
  1. 01|はじめに
  2. 02|そもそもJWTとは
  3. 03|実際にJWTを作ってみよう
  4. 04|JWT作るの面倒じゃない?
  5. 05|さいごに

1. はじめに

Pythonでお馴染みになりつつある(と信じている)エンジニアの眞嶋です。

今回はOAuthやOpenIDほど複雑ではないですが、公開する上で一定の認証レベルを担保したい際に有効な手法としてJWT認証をご紹介します。

初の2部構成でJWT認証について実装まで解説していきます。

2. そもそもJWTとは

JWT認証と銘打った直後ですが、JWTというのは本来認証方式ではなく、認証の中で使用されるデータ形式のことを指しています。

「JSON Web Token」というのが正式名称で、その名前の通りトークンの形式として定義されたものになります。

構造としては、「ヘッダ」「ペイロード」「署名」の3つをつなぎ合わせ、最後に電子署名を付与した構造になっています。

3. 実際にJWTを作ってみよう

ここまでは様々なところで紹介されている内容ですが、文字だけ見てもよくわからないと思います。(僕自身がそうでした)

ということでエンジニアらしく、ここは実際にコードを書いて解説していきます。

{
"alg": "HS256",
"typ": "JWT"
}
{
"iat": 1653199095,
"jti": "da5dd8a6-15c5-4197-9f6b-cc0f6051dcf2",
"type": "access",
"sub": "U0000000120",
"nbf": 1653199095,
"exp": 1653199995
}

これらをそれぞれBase64エンコードした文字列を「.(ドット)」繋ぎにすることで、署名なしトークンを作成します。

上記の値を使って実際に試してみましょう。



from base64 import urlsafe_b64encode
import json

def to_base64(data):
data_bytes = json.dumps(data).encode()
encoded = urlsafe_b64encode(data_bytes)
return encoded

header = {
"alg": "HS256",
"typ": "JWT",
}
payload = {
"iat": 1653199095,
"jti": "da5dd8a6-15c5-4197-9f6b-cc0f6051dcf2",
"type": "access",
"sub": "U0000000120",
"nbf": 1653199095,
"exp": 1653199995
}

header_base64 = to_base64(header)
payload_base64 = to_base64(payload)

print((header_base64 + b'.' + payload_base64).decode())

eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpYXQiOiAxNjU
zMTk5MDk1LCAianRpIjogImRhNWRkOGE2LTE1YzUtNDE5Ny05ZjZiLWN
jMGY2MDUxZGNmMiIsICJ0eXBlIjogImFjY2VzcyIsICJzdWIiOiAiVTA
wMDAwMDAxMjAiLCAibmJmIjogMTY1MzE5OTA5NSwgImV4cCI6IDE2NTMxOTk5OTV9

 

実はこれがJWTのデータ本体になります。

しかし、このままではこのデータが正しいものであることを保証できないので、認可トークンとして扱うにはセキュリティ的にリスクが高すぎて使えたものではありません。

このデータに対して電子署名を施すことで、サーバー側もクライアント側もこれが「正しく生成された」JWTであることを確認することができます。


from base64 import urlsafe_b64encode
import json
import hashlib
import hmac

def to_base64(data):
data_bytes = json.dumps(data).encode()
encoded = urlsafe_b64encode(data_bytes)
return encoded.decode()

header = {
"alg": "HS256",
"typ": "JWT",
}
payload = {
"iat": 1653199095,
"jti": "da5dd8a6-15c5-4197-9f6b-cc0f6051dcf2",
"type": "access",
"sub": "U0000000120",
"nbf": 1653199095,
"exp": 1653199995
}

header_base64 = to_base64(header)
payload_base64 = to_base64(payload)
secret_key = b'SecretKey'

no_signature = header_base64 + '.' + payload_base64

signature_bytes = urlsafe_b64encode(hmac.new(secret_key, no_signature.encode(), hashlib.sha256).digest())
signature = signature_bytes.decode().rstrip('=')

jwt = header_base64 + '.' + payload_base64 + '.' + signature
print(jwt)

eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpYXQiOiAxNjUzMTk5MDk1LCAian
RpIjogImRhNWRkOGE2LTE1YzUtNDE5Ny05ZjZiLWNjMGY2MDUxZGNmMiIsICJ0eXBlIjogImF
jY2VzcyIsICJzdWIiOiAiVTAwMDAwMDAxMjAiLCAibmJmIjogMTY1MzE5OTA5NSwgImV4cCI6ID
E2NTMxOTk5OTV9.EyIdq6nIfBDQ4_dcA6airyBrIfafoQ6C-SuxFEHD2n4

 

これで電子署名を施したJWTトークンの完成です。

JWTの内容が正しいかを確認するには、「https://jwt.io/」が便利です。

4. JWT作るの面倒じゃない?

ここまで見てきた中で、JWTを作るのって結構手間がかかるんだなぁと感じた方もいることでしょう。

先ほどは構造を解説する目的でコードを書いたので長くなりましたが、ただ作るだけであればもっと簡単に作る方法もあります。

今回の趣旨と外れてしまうので割愛しますが、興味のある方は「PyJWT」と検索すると多くの記事が見つかりますのでご参考まで。

5. さいごに

本記事ではJWTについて理解を深めてもらおうと思い、ライブラリに頼らないJWT生成を行いました。

実装する際にはライブラリを使って、車輪の再発明をなるだけ減らしてもらう方がいいのですが、それに頼りすぎると「よくわからんが動いてるからヨシッ!」という某現場猫のようになってしまうので注意する必要がありますね。

さて、後編ではいよいよFlaskでの実装を行っていきますのでお楽しみに!

ちなみにFlaskではJWT生成と認証チェックまでまとめて対応してくれる便利なライブラリがあるので、それを使って実装していきます。(働き猫の話はどこいった!?)

RECRUIT 採用情報

「eビジネスに関わる全ての人を幸せにする」
私達とともに新たな時代をつくりませんか?