ジャンゴ(django)でJWTを使う方法

2019-06-06

ジャンゴ(django)のプロジェクトでAPIを使う時、JWT(Json Web Token)認証を使う方法について説明します。

概要

リアクトネイティブ(React Native)で開発したアプリ(App)のログイン、情報更新などのAPIサーバーをジャンゴ(django)を使って開発してみようかと思っています。この時、アプリ(App)とAPIサーバーで良く使ってるJWT(Json Web Token)認証をジャンゴ(django)を使って実装してみようかと思っていました。このブログではDjango REST framework JWTを使ってジャンゴ(django)のJWTを実装する方法について説明します。

このブログで使ったソースコードはgithubに公開されております。下記のリンクで確認できます。

ジャンゴ(django)プロジェクトの準備

以前のブログシリーズでジャンゴ(django)のプロジェクトを使う方法について説明しました。ジャンゴ(django)を使ってプロジェクトを構成する方法について詳しく内容は下記のリンクを確認してください。

このブログではジャンゴ(django)のインストールやプロジェクトの設定などについては説明しません。簡単にプロジェクトが進めるレベルで説明します。

下記のコマンドでジャンゴ(django)のプロジェクトを生成します。

django-admin startproject django_response_model_to_json

下記のブログを参考してデーターベース連動やテーブルを作ります。

データーベースの連動が終わったら下記のコマンドで管理者(superuser)を作ります。

python manage.py createsuperuser

下記のブログを参考してBlogジャンゴ(django)のアプリとPostモデル(Model)を生成します。

そして下記のブログを参考してテストデーターを追加します。

Django REST framework JWTインストール

JWTの実装は結構面倒な部分が多いです。それで殆どのプラットフォームには良く作ったモジュールやライブラリが存在します。私たちはここでDjango REST framework JWTを使ってJWTを実装します。

下記のコマンドでDjango REST framework JWTをインストールします。

pip install djangorestframework djangorestframework-jwt
  • djangorestframework: ジャンゴ(django)のRestful APIをもっと簡単に使えるようにしてくれるむジュールです。
  • djangorestframework-jwt: ジャンゴ(django)のRestful APIでJWTを簡単に実装できるようにしてくれるモジュールです。

インストールが終わったら忘れなくrequirements.txtを更新します。

pip freeze > requirements.txt

Django Rest framework JWT設定

Django Rest framework JWTを設定するためdjango_jwt/settings.pyを開いて下記のように修正します。

...
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_ALGORITHM': 'HS256',
    'JWT_ALLOW_REFRESH': True,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28),
}
...

もうちょっと詳しく見たら、

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

上部でインストールしたdjangorestframeworkに関する設定です。

  • ログインしたかどうかを確認するクラス(DEFAULT_PERMISSION_CLASSES)をrest_framework.permissions.IsAuthenticatedで使えるように設定します。
  • ログインと関連するクラス(DEFAULT_AUTHENTICATION_CLASSES)をJWT(rest_framework_jwt.authentication.JSONWebTokenAuthentication)を使えるように設定します。
JWT_AUTH = {
    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_ALGORITHM': 'HS256',
    'JWT_ALLOW_REFRESH': True,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=28),
}
  • JWT_SECRET_KEY: JWTの秘密鍵(Secret Key)の設定です。ここではジャンゴ(django)と同じ秘密鍵を使えるようにしましたが本当は別の鍵を使えるように設定することをお勧めします。
  • JWT_ALGORITHM: JWTの暗号化に使うアルゴリズムを指定します。
  • JWT_ALLOW_REFRESH: JWTトークを更新(Refresh)できるようにするかを決めます。
  • JWT_EXPIRATION_DELTA: JWTトークンの有効期限を設定します。
  • JWT_REFRESH_EXPIRATION_DELTA: JWTトークンの更新の有効期限です。

JWT_EXPIRATION_DELTAとJWT_REFRESH_EXPIRATION_DELTAが良く理解できないですね。上のように設定するとJWTトークンは7日以内に更新(Refresh)しないとJWTトークンを使えなくなってログアウトされます。また7日以内に頑張って更新(Refresh)しても28日後には更新(Refresh)出来ないです。つまり、頑張って更新しても28日後にはログアウトされることを意味します。

Django Rest framework JWTのためURL設定

JWT認証のためURLを追加する必要があります。django_jwt/urls.pyを開いて下記のように修正します。

from django.contrib import admin
from django.urls import path, include
from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token, refresh_jwt_token

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/token/', obtain_jwt_token),
    path('api/token/verify/', verify_jwt_token),
    path('api/token/refresh/', refresh_jwt_token),
    path('api/blog/', include('blog.urls'))
]
  • from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token, refresh_jwt_token: JWT認証に必要な物を読んできます。
  • obtain_jwt_token: JWTトークンを発行する時使います。 (path('api/token/', obtain_jwt_token))
  • verify_jwt_token: JWTトークンが有効かどうか確認する時使います。(path('api/token/verify/', verify_jwt_token))
  • refresh_jwt_token: JWTトークンを更新(Refresh)する時使います。(path('api/token/refresh/', refresh_jwt_token))
  • path(‘api/blog/’, include(‘blog.urls’)): 私たちが作ったBlogアプリのURLと連携します。

私たちが作ったBlogアプリのURLを作るためblog/urls.pyを開いて下記のように修正します。

from django.urls import path
from . import views

urlpatterns = [
    path('posts/', views.posts, name='posts'),
]

ビュー(View)を作る

実際にJWT認証を使うAPIを作ってみましょう。blog/views.pyを開いて下記のように修正します。

from django.shortcuts import render
from django.core import serializers
from django.http import HttpResponse
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from .models import Post


@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
@authentication_classes((JSONWebTokenAuthentication,))
def posts(request):
    posts = Post.objects.filter(
        published_at__isnull=False).order_by('-published_at')
    post_list = serializers.serialize('json', posts)
    return HttpResponse(post_list, content_type="text/json-comment-filtered")

今まで作ってみたビュー(View)と形が違います。簡単にみてみます。

from django.shortcuts import render
from django.core import serializers
from django.http import HttpResponse
...
from .models import Post
...
def posts(request):
    posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    post_list = serializers.serialize('json', posts)
    return HttpResponse(post_list, content_type="text/json-comment-filtered")

この部分は以前のブログ(ジャンゴ(django)のモデル(Models)をJSONタイプでレスポンス(Response)する)で説明しました。モデル(Models)の結果(QuerySet)をJSONでレスポンス(Response)するための部分です。じゃ、新しく追加された部分を見てみましょう。

...
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
...
@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
@authentication_classes((JSONWebTokenAuthentication,))
def posts(request):
...
  • from rest_framework.decorators import api_view, permission_classes, authentication_classes: 私たちがインストールしたdjangorestframeworkが提供してるパイソンのデコレーター(Python Decorator)です。デコレーター(Decorator)を説明すると長くなるので、簡単に理解すると前処理と考えたら簡単と思います。
  • from rest_framework.permissions import IsAuthenticated: ログインしたかどうかをチェックする時使います。
  • from rest_framework_jwt.authentication import JSONWebTokenAuthentication: JWT認証を確認する時使います。
  • @api_view(['GET']): 上で説明したデコレーター(Decorator)です。下にあるファンクション(def posts(request):)を実行する前この部分が実行されます。このデコレーター(Decorator)はGETリクエストかチェックしてGETじゃないとエラーをJSONタイプでレスポンスします。
  • @permission_classes((IsAuthenticated, )): 権限をチェックします。ここではログインしたかどうかだけチェックします。
  • @authentication_classes((JSONWebTokenAuthentication,)): JWTトークンを確認します。トークンに問題があったらエラーをJSONタイプでレスポンスします。

確認

今まで作った内容をPostmanで確認してみます。下記のコマンドでジャンゴ(django)のテストサーバーを実行します。

python manage.py runserver

そしてPostmanを開いて下記のようにhttp://localhost:8000/api/blog/posts/へGETリクエストを送ります。

ジャンゴ(django)でJWTを使う - JWT認証エラー

そしたら上のようにエラーメッセージが確認できます。私たちが作ったAPIがJWT認証をちゃんと使ってることが確認できます。

それじゃ、JWT認証で情報を持てくるためJWTトークンを発行してみます。http://localhost:8000/api/token/へPOSTでusernamepasswordを設定してリクエストを送ります。ここで使ってusernameとpasswordは私たちが作ったジャンゴ(django)のsuperuserです。(ジャンゴの管理画面にログインする時使ったusernameとpassword)

ジャンゴ(django)でJWTを使う - JWTトークンの発行

上のようにJWTトークンがうまく発行されたことが確認できます。発行されたJWTトークンを使ってもう一回上のリクエスト(http://localhost:8000/api/blog/posts/)を送ってみます。ここではリクエストのHeaderAuthorizationを設定して送ります。jwt [jwt key]の形式で入れます。

ジャンゴ(django)でJWTを使う - JWTトークンを使ってデーター取得

以前と違ってデーターが取得できることが確認できます。次はJWTトークンを検証する方法と更新する方法について見てみます。JWTトークンを検証する方法はhttp://localhost:8000/api/token/verify/へPOSTで上で発行して貰ったトークンを送信します。

ジャンゴ(django)でJWTを使う - JWTトークン検証

そしたら、上のように問題がないことが確認できます。JWTトークンに問題があったら下記のようにエラーメッセージが帰ってきます。

ジャンゴ(django)でJWTを使う - JWTトークンの検証エラー

じゃ、JWTトークンを更新(Refresh)してみましょうか?http://localhost:8000/api/token/refresh/へPOSTで既に発行して貰ったJWTトークを送ります。

ジャンゴ(django)でJWTを使う - JWTトークン更新

そしたら上のように新しJWTトークンが発行されます。もしJWTトークンを更新しなかった、有効期限で切れた場合、下記のようなエラーメッセージが帰ってきます。

ジャンゴ(django)でJWTを使う - JWTトークンエラー

完了

これでジャンゴ(django)でJWTを使う方法について見てみました。今からはJWTトークンを使ってアプリとサーバーの通信やフロントエンドとサーバー間通信へ使えます!

参考

Buy me a coffeeBuy me a coffee
Posts