【django】メールアドレスを使用したログイン認証のカスタマイズ方法【authenticate】

昨今のwebサービスでのアカウントへログイン認証方法は、メールアドレスとパスワードの2つを用いた認証が当たり前となっています。

しかしながらdjangoでは、公式チュートリアルを読んでもメールアドレスを使用したログイン認証の作成方法はなく、あるのはauthenticateでユーザー名とパスワードを用いたものしかありません。

昨今のトレンドで、usernameとpasswordは利用者にとってストレスでしかありません。

googleもappleもamazonもメルカリもログインに必要なのは、メールアドレスとパスワードです。

今回は、私ヒロヤンが独自にauthenticateを使用してのメールアドレスとパスワードでログイン認証をdjangoにカスタマイズする方法を紹介します。

キーワード

django authenticate email instead of username

おすすめする人

✔️ユーザー名ではなくメールアドレスで認証ログインを実行したい人

何が問題なのか

djangoの予め用意されているauthenticateメソッドは、ログインに必要なのが、username(ユーザー名)とemail(メールアドレス)なのです。

公式: https://docs.djangoproject.com/ja/2.2/topics/auth/default/#authenticating-users

ログイン認証にユーザー名とパスワードを用いているサービスは技術が2000年代で止まったままのサービスです。

デザインしかり機能もそうですが、このような機能のトレンドは業界を牽引する大手がルールを作り、それに後続が引っ張られるといっても過言ではありません。

シンプルにそのようなUX(ユーザーエクスペリエンス)を真似すれば、利用者のストレスもなくなり利用者離れも少なくすることができます。

ゴール

早速ですがカスタマイズ方法を紹介していきます。

ゴールとしては下記のように、メールアドレスとパスワードを用いてログインができるようになります。

(CSSは割愛します。)

カスタマイズ手順

html

htmlには特にこれといった変わり映えするような技術は使用しません。

sample.html

<form method="post">
<h1>ログイン</h1>
<dl>
    <div>
        <dt>メールアドレス</dt>
        <dt>{{ form.email }}{{ form.email.errors }}</dt>
    </div>
    <div>
        <dt>パスワード</dt>
        <dt>{{ form.password }}{{ form.password.errors }}</dt>
    </div>
    <div>
        <div>{{ form.non_field_errors }}</div>
        <button type="submit">ログイン</button>
    </div>
</dl>
</form>

forms.py

ログイン認証に関してdjangoユーザーなら一般的に、formsかviewsのどちらかに書くことになりますが、私の場合は認証及びリデーションは全てformsに統一させてることをモットーにしていますので、今回もformsに書いていきます。

そして今回の肝である、メールアドレスを使用したログイン認証のカスタマイズ方法もformsに記入をしていきます。

先ずはコードを紹介しますので中身の説明はコードの後ろで行います。

forms.py

from django import forms
from django.core.exceptions import ValidationError

# ログイン
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate

# 例外処理
from django.core.exceptions import ObjectDoesNotExist

User = get_user_model()

class SigninForm(forms.Form):
    """ログインフォーム
    """
    email = forms.EmailField(
        required=True,
        max_length=255,
        min_length=3,
        widget=forms.EmailInput(
            attrs={
                'placeholder': 'your-email@example.com',
            }
        )
    )
    password = forms.CharField(
        required=True,
        max_length=255,
        min_length=6,
        widget=forms.PasswordInput(
            attrs={
                'placeholder': '******',
            }
        )
    )
    def clean(self):
        cleaned_data = super(SigninForm, self).clean()
        if 'email' and 'password' in cleaned_data:
            try:
                user = User.objects.get(email=cleaned_data['email'])
            except:
                raise ValidationError('メールアドレスかパスワードが間違っています。')
            auth_result = authenticate(
                username=str(user),
                password=cleaned_data['password']
            )
            if not auth_result:
                raise ValidationError('メールアドレスかパスワードが間違っています。')
            cleaned_data['username'] = str(user)
            return cleaned_data
    def clean_email(self):
        email = self.cleaned_data['email']
        return email
    def clean_password(self):
        password = self.cleaned_data['password']
        return password
    def cleaned_username(self):
        username = self.cleaned_data['username']
        return username
  1. 6行目でauthenticateを呼び出します
  2. 5行目及び11行目を見るとわかるようにユーザーはdjangoのデフォルトを使用します
  3. 40行目のUser.objects.getでユーザーが存在するかクエリーを叩きます。基本的にgetは存在するデータが1つと期待するときに使用します
  4. 3.で存在しない時の例外処理を9行目のObjectDoesNotExistから呼び出し処理を39行目から42行目までに書いています
  5. メールアドレスが間違っているが、パスワードが正しい。もしくはメールアドレスが正しいがパスワードが間違っている。このどちらのパターンでもエラーメッセージは「メールアドレスかパスワードが間違っています。」に設定します。要はセキュリティでどちらか片方が正しいことを推測されることを防ぐためです。
  6. 57行目でわざわざusernameを用意したのはこの後のviews側でユーザーを再度認証させてログインさせるための準備になります

4.のgetが存在しない時の例外処理に関してですが、きちんと公式に乗っ取った適切な処理になりますので、以下にリンクを載せておきます。

https://docs.djangoproject.com/ja/2.2/ref/models/querysets/#django.db.models.query.QuerySet.get

views.py

最後にレンダリングする前のviews側の処理になりますが、formsで既にauthenticateの認証が取れたのであとはlogin(ログイン)メソッドを使ってユーザーをログインするコードを書いてしまえばOKです。

authenticateで必要なusernameもforms側で処理済みで用意されているので安心して使用できます。

views.py

from django.shortcuts import render
from django.http.response import HttpResponseedirect
from django.urls import reverse
from .forms import SigninForm
# ログイン関係
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate, login, logout

User = get_user_model()

def signin(request):
    """ログイン
    """
    if request.method != 'POST':
        if str(request.user) != 'AnonymousUser':
            form = ''
        else:
            form = SigninForm()
    else:
        form = SigninForm(request.POST)
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']
            username = form.cleaned_data['username']
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                return HttpResponseRedirect(reverse('example:toppage'))
            else:
                pass
    context = {
        'form': form
    }
    return render(request, 'example/toppage.html', context)

説明は以上です。

最後に

いかがでしたでしょうか。

以上が、「【django】メールアドレスを使用したログイン認証のカスタマイズ方法【authenticate】」の紹介記事になります。

セキュリティも万全なので是非サイトに使用して見てはいかがでしょうか?

またもし他にauthenticateでメールアドレスを使用したログイン方法があるならばコメント頂ければと思います。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です