Django

Django Vuejs JWT認証の実装を開始(Total:2day9h)

ほとんどのシステムで必須となるログイン機能を実装する

JWT認証では、ヘッダに毎回リクエストを含めて送信することでログインを保証するやり方です。

サードパーティーを入れて実装をしていこうと思います。

djoser と djangorestframework_simplejwt をインストール

https://djoser.readthedocs.io/en/latest/getting_started.html

とっても便利なログイン機能周りを実装してくれます。

jwt認証周りを実装してくれる機能がいっぱいのsimplejwtをインストールする。

https://pypi.org/project/djangorestframework-simplejwt/


pip install -U djoser
pip install -U djangorestframework_simplejwt

settings.py のINSTALLED_APPSにdjooser を追加する

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #サードパーティー
    'rest_framework',
    'djoser',
    
    #アプリ
    'users',
]

settings.py にrest_frameworkの設定を追加する

REST_FRAMEWORK = {
    # パーミッションチェック
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    # 認証方法をsimplejwtに指定
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ]
}

SIMPLE_JWT = {
    # アクセストークンの有効時間
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    # 更新トークンの有効期間
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    # リフレッシュトークンがされるか
    'ROTATE_REFRESH_TOKENS': True,
    # ブラックリストに追加
    'BLACKLIST_AFTER_ROTATION': True,
    # ログイン時に更新しない 遅延対策
    'UPDATE_LAST_LOGIN': False,

    # 暗号化方式
    'ALGORITHM': 'HS256',
    # 生成されたトークンに署名キー Djangoのキーはおすすめしない
    'SIGNING_KEY': SECRET_KEY,
    # 生成されたトークンの内容を検証
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    # 有効期限
    'LEEWAY': 0,

    # 認証が必要で受け入れられる許可ヘッダータイプ
    'AUTH_HEADER_TYPES': ('JWT',),
    # 認証に使用される許可ヘッダー
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    # 'USER_ID_FIELD': 'id',
    # 'USER_ID_CLAIM': 'user_id',
    # ユーザーが認証を許可されているかどうかを判断する
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    # 認証を証明できるトークンタイプ
    'AUTH_TOKEN_CLASSES': ('users.models.JWTAccessToken',),
    # トークンのタイプを格納する名
    'TOKEN_TYPE_CLAIM': 'token_type',
}

url.pyにdjaoserを設定する(あとで、djoserは使わない方法に変更)

configのurls.pyにdjoser用のurlを追加する

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/auth/', include('djoser.urls')), #追加
    path('api/auth/',include('djoser.urls.jwt')), #追加
]

これで、バックエンドの準備が完了みたいです…不安(笑)

runserverでエラーが発生

ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
djoser 2.1.0 requires djangorestframework-simplejwt<5.0.0,>=4.3.0, but you have djangorestframework-simplejwt 5.0.0 which is incompatible.

ここでちょっと考える…
djoserを使わずに実装するように公式サイトを参考にしていくことに切替ます。
https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html

djoserに向くようにしていたurl.pyをusersアプリに行くように変更します。
インストールアプリからdjoserを削除します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #サードパーティー
    'rest_framework',
    
    #アプリ
    'users',
]
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('users.urls')),
]

次に、usersでtokenのリクエストを受けるようにurl.pyファイルを作成します。

from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path('authen/login/', TokenObtainPairView.as_view(), name='token.login'),
    path('auth/refresh/', TokenRefreshView.as_view(), name='token.refresh'),
]

あっ、たったこれだけだったんだ…
djoserには色んな機能があるみたいですが、今回はログイン認証で使うだけなのでこれでいいのかもしれません。

ここで、runserverを実行してみます。


エラーが発生

Unknown command: 'pytz

pip install pytz

これで、サーバー起動に成功しました!

よかったよかった(⌒∇⌒)

Vue.jsフロント側のログイン機能を実装していく!

ここからが、本当に大変かもしれません…

いや、大変になるだろう…

それでは、Vue.jsで実装をやります。

axios をインストールする

front フォルダにいる状態で、下記を実行

npm install axios

Login画面を作ろう!

ログイン機能の実装方法を調べていましたが、ちょっと行き詰った感があったので、まずはログイン画面の作成にチャレンジ!

今回使用するVuetifyは、マテリアルデザインになっているので、基本的に変に触ること(統一性をなくしてしまう…)はやめておいて、すすめます。


  1. ポイント

    • username をDjango側に送信するために入力ができる必要がある
    • password  をDjango側に送信するために入力ができる必要がある
    • password  はセキュリティ上、入力時に見えないようにしておくが、確認のために表示もできるように切替可能とする
    • username と passwordを送信する機能
    • ログインが失敗した時にログイン失敗の通知をユーザーにする
    • ログインが成功した時に、tokenを受け取り、リクエスト時に毎回送信されるようにする
    • ログインがされていない時は、ログイン画面に強制送還されること
    • tokenの期限が切れていたらログイン画面に強制送還されること

Views/authen/Login.vue フォルダとファイルを作成

ログイン画面用のフォルダとファイルを作成しました!

コードは、途中ですが、画面はある程度表示できるようになりました✌

アイコンとかもすぐに実装できるので、便利です❕まぁ、自分で実装ってなった時にどれだけ大変なのかわかっていないので便利さはわかりませんが(笑)

パスワードの表示非表示は、真偽値で調整することで実装しました。

見た目ができただけで、リクエストを送る機能も実装していませんが、めっちゃテンションあがります!
自分でモダンなデザインができて、入力する時には、Usernameと表示されていうテキストが、枠に移動したりして本当に楽しい(⌒∇⌒)

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'

@Component({})
export default class Login extends Vue {
    username= null
    password= null
    showPassword= false
}
</script>

<template>
    <div id="login" class="login-page">
        <!-- 幅と表示を真ん中指定 -->
        <v-card width="400px" class="mx-auto mt-5">
            <v-card-title>
                <h1 class="display-1">Login</h1>
            </v-card-title>
            <v-card-text>
                <v-form>
                    <v-text-field
                        prepend-icon="mdi-account-circle"
                        v-model="username"
                        label="Username"
                        outlined
                        required
                    ></v-text-field>
                    <!-- タイプをtextとpasswordがアイコンをクリックすることで、真偽値が変わることで表示と非表示を切り替えアイコンも切り替える -->
                    <v-text-field
                        v-bind:type="showPassword ? 'text' : 'password'"
                        @click:append="showPassword = !showPassword"
                        prepend-icon="mdi-lock" 
                        v-bind:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
                        label="Password"
                        v-model="password"
                        outlined
                        required
                    ></v-text-field>
                    <v-btn class="info" type="submit">ログイン</v-btn>
                </v-form>
            </v-card-text>
        </v-card>
    </div>
</template>
<style lang="scss" scoped></style>

通常、templateが上にあって、scriptタグが下にある場合がほとんどで、その例に従って書き始めたのですが、scriptタグの方がたくさんみることが多いので上に持ってきました。

これは、完全に好みですね。動けばなんでもいいですね

ブログ書きながらやるから時間がかかるけど誰かの役に立ってれば

困っている人
プログラミングを習い始めると自分が、とても遅くなかなかコードが実装できないから向いていないなぁ

って、思うことがよくあります…本当によくあります💦

ほとんどの人が最初は、独学でUdemyや参考書で勉強しながらかスクールに行きながらが多いと思います。

困っている人
あんなにすぐにコードが何でかけるんだろう…自分はまだ何にも進んでいないのに…

こんな風に考えがちだと思います。

独学で勉強しながら実装していく場合、どれだけ時間がかかるかを知ってもらえたらと思います。

自分で作成した場合、機能を流用したりすることができるので、実装時間は極端に短くなります。

Djangoのみで開発して時、最初のころは簡単な画面でも1画面30時間以上かけながら、悪戦苦闘しながら作っていましたが、何度もやっていると1画面2時間もかからず作れるようになります。

いずれ必ず早くはなります。早くなるスピードが人によって異なることはあるけど、1部の天才的なひとと自分を比べることは意味がないので止めておきましょう

メモ

人と比べるのじゃなくて少し前の自分と比べるといいかも

コードを書いている時間より、本や公式ドキュメントを読んだり、ググったりGitHubで公開されているコードを見たりして、読み解きする時間が圧倒的に長いです…

ログイン画面のコード自体は、1時間ぐらいちょっとでかけましが、残り4時間が読み解き調べる時間でした💦

-Django

© 2022 ごろう@縁紡ぐ