Django-メールでログイン


86

djangoがユーザー名ではなくメールでユーザーを認証するようにしたい。1つの方法は、ユーザー名の値として電子メールの値を提供することですが、私はそれを望んでいません。理由は、私はURLを持っている/profile/<username>/ので、URLを持つことができません/profile/abcd@gmail.com/

もう1つの理由は、すべての電子メールが一意であるということですが、ユーザー名がすでに使用されている場合があります。したがって、ユーザー名をとして自動作成していますfullName_ID

Djangoにメールで認証させるように変更するにはどうすればよいですか?

これが私がユーザーを作成する方法です。

username = `abcd28`
user_email = `abcd@gmail.com`
user = User.objects.create_user(username, user_email, user_pass)

これが私がログインする方法です。

email = request.POST['email']
password = request.POST['password']
username = User.objects.get(email=email.lower()).username
user = authenticate(username=username, password=password)
login(request, user)

最初にユーザー名を取得する以外に、ログインの他の方法はありますか?

回答:


109

カスタム認証バックエンドを作成する必要があります。このようなものが機能します:

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

次に、そのバックエンドを設定で認証バックエンドとして設定します。

AUTHENTICATION_BACKENDS = ['path.to.auth.module.EmailBackend']

更新されました。すでにのModelBackendようなメソッドを実装しているため、から継承しget_user()ます。

こちらのドキュメントを参照してください:https//docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend


6
django 1.9.8を使用すると、エラーが発生します: 'EmailBackend'オブジェクトに属性 'get_user'がありません。このstackoverflow.com/a/13954358/2647009
baltasvejas 2016年

1
このコードが機能するDjangoのバージョンを指定してください。get_userメソッドがないことに不満を言う人もいます。
YounesHenni博士2017年

2
ユーザーが持っていることを確認するために、if user.check_password(password):DjangoがデフォルトでModelBackendif user.check_password(password) and self.user_can_authenticate(user):を介して行うことを含めるだけではなく、おそらく必要ですis_active=True
JMQ

1
ソースコードにDjangoの緩和策が含まれていないため、これはタイミング攻撃に対して脆弱ではありませんか?
ガブリエルガルシア

これで、リクエストの本文にユーザー名やパスワードなどのフィールドが含まれるようになります。メールとパスワードに変更する方法はありますか?
カロル

55

新しいプロジェクトを開始する場合、djangoはカスタムユーザーモデルを設定することを強くお勧めします。(https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-projectを参照してください

実行した場合は、ユーザーモデルに次の3行を追加します。

class MyUser(AbstractUser):
    USERNAME_FIELD = 'email'
    email = models.EmailField(_('email address'), unique=True) # changes email to unique and blank to false
    REQUIRED_FIELDS = [] # removes email from REQUIRED_FIELDS

その後、authenticate(email=email, password=password)動作をauthenticate(username=username, password=password)停止しながら動作します。


3
createsuperuserを実行すると、これ自体がエラーをスローします。TypeError:create_superuser()1つの必須の位置引数がありません: 'username'。あなたは、カスタム・ユーザー・マネージャを使用する必要があります class MyUserManager(BaseUserManager): def create_superuser(self, email, password, **kwargs): user = self.model(email=email, is_staff=True, is_superuser=True, **kwargs) user.set_password(password) user.save() return user
ミハル・ホルブ

18
ここでの詳細な手順:fomfus.com/articles/...
user2061057

1
繰り返しになりますが、再利用可能なアプリを作成する場合、Djangoのドキュメントはカスタムユーザーモデルを使用しないようにアドバイスしています。
djvg

14

Django3.xのメール認証

デフォルトのユーザー名とパスワードの認証の代わりに電子メール/ユーザー名とパスワードを認証に使用するには、ModelBackendクラスの2つのメソッド、authenticate()とget_user()をオーバーライドする必要があります。

get_userメソッドはuser_id(ユーザー名、データベースIDなどですが、ユーザーオブジェクトに固有である必要があります)を受け取り、ユーザーオブジェクトまたはNoneを返します。電子メールを一意のキーとして保持していない場合は、query_setに対して返される複数の結果を処理する必要があります。以下のコードでは、返されたリストから最初のユーザーを返すことでこれを処理しています。

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try: #to allow authentication through phone number or any other field, modify the below statement
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

        return user if self.user_can_authenticate(user) else None

デフォルトでは、AUTHENTICATION_BACKENDSは次のように設定されています。

['django.contrib.auth.backends.ModelBackend']

settings.pyファイルで、デフォルトを上書きするには、下部に以下を追加します。

AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)

これは素晴らしいです、ありがとう。プロジェクト、テンプレート、フォーム、ビューなどをほぼ完了したので、最初からやり直すのはそれほど魅力的ではありません。メールアドレスで認証できるようになりました。ユーザー名フィールドを削除して、認証とテンプレートに表示されるフォームに含まれないようにする方法はありますか?
ムーンレイカー

11

ユーザー名/メールのいずれかがユーザー名フィールドで機能するという同様の要件がありました。誰かがこれを行う認証バックエンドの方法を探している場合は、次の作業コードを確認してください。メールのみが必要な場合は、クエリセットを変更できます。

from django.contrib.auth import get_user_model  # gets the user_model django  default or your own custom
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q


# Class to permit the athentication using email or username
class CustomBackend(ModelBackend):  # requires to define two functions authenticate and get_user

    def authenticate(self, username=None, password=None, **kwargs):
        UserModel = get_user_model()

        try:
            # below line gives query set,you can change the queryset as per your requirement
            user = UserModel.objects.filter(
                Q(username__iexact=username) |
                Q(email__iexact=username)
            ).distinct()

        except UserModel.DoesNotExist:
            return None

        if user.exists():
            ''' get the user object from the underlying query set,
            there will only be one object since username and email
            should be unique fields in your models.'''
            user_obj = user.first()
            if user_obj.check_password(password):
                return user_obj
            return None
        else:
            return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

また、settings.pyにAUTHENTICATION_BACKENDS =( 'path.to.CustomBackend'、)を追加します


これは、1.11から2.1.5にアップグレードするまではうまくいきました。このバージョンで動作しない理由はありますか?
ゼロヘッジ

@zerohedgeは、認証メソッドのパラメーターにリクエストを追加します。docs.djangoproject.com/en/2.2/topics/auth/customizing/…を
Van

また、タイミング攻撃にさらされる可能性があります。このような脆弱性を防ぐために、Djangoの実装を厳密に模倣することを試みる価値があります。 github.com/django/django/blob/master/django/contrib/auth/...
ガブリエル・ガルシア

これにより、非アクティブなユーザーも認証できるようになります。
ガブリエルガルシア

5

Django 2.x

上記のGaneshがdjango2.xについて述べたように、認証メソッドにはリクエストパラメータが必要になりました。

# backends.py
from django.contrib.auth import backends, get_user_model
from django.db.models import Q
UserModel = get_user_model()


class ModelBackend(backends.ModelBackend):

    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        try:
            # user = UserModel._default_manager.get_by_natural_key(username)
            # You can customise what the given username is checked against, here I compare to both username and email fields of the User model
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user
        return super().authenticate(request, username, password, **kwargs)

プロジェクト設定にバックエンドを追加します

# settings.py
AUTHENTICATION_BACKENDS = ['path.to.ModelBackend']

カスタムユーザーモデルでは、アクティブな検証済みユーザーに対して一意の電子メールを作成する必要があります。これは、次のような方法で簡単に実行できます。

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    objects = UserManager()
    email = models.EmailField(_('email address'), unique=True)

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        db_table = 'auth_user'
        swappable = 'AUTH_USER_MODEL'

ただし、他の誰かが自分の電子メールを使用するのをブロックしている人を防ぐには、代わりに電子メール検証を追加し、登録とログインのプロセスで、電子メールが一意でない可能性があることを考慮に入れる必要があります(おそらく、新しいユーザーが既存の検証済みの電子メールアドレスを使用するのを防ぎます)。


4

Django2.Xの電子メールとユーザー名の認証

これはよくある質問であることを念頭に置いて、これはDjangoソースコードを模倣するカスタム実装ですが、ユーザー名または電子メールのいずれかでユーザーを大文字と小文字を区別せずに認証しタイミング攻撃 保護を維持し、非アクティブなユーザーを認証ません

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

        return user if self.user_can_authenticate(user) else None

常に設定を追加することを忘れないでください。正しい認証バックエンド


1
UserModel().set_password(password)攻撃者が、ほぼ同じ量の暗号化作業を実行することによって、ユーザーが存在するか存在しないかを判断するのを防ぐためにあるという私の理解は正しいですか(これはあなたが意図したタイミング攻撃だと思います)?
グランドプーバ

2

ModelBackendクラスをカスタマイズする必要があります。私の簡単なコード:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

class YourBackend(ModelBackend):

  def authenticate(self, username=None, password=None, **kwargs):
    UserModel = get_user_model()
    if username is None:
        username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
        if '@' in username:
            UserModel.USERNAME_FIELD = 'email'
        else:
            UserModel.USERNAME_FIELD = 'username'

        user = UserModel._default_manager.get_by_natural_key(username)
    except UserModel.DoesNotExist:
        UserModel().set_password(password)
    else:
        if user.check_password(password) and self.user_can_authenticate(user):
            return user

そして、settings.pyファイルに以下を追加します

AUTHENTICATION_BACKENDS = ['path.to.class.YourBackend']

コードを更新して、django 2.1.1のメソッドにrequestパラメーターを含めますauthenticate
Ganesh

2

Django2.xの電子メールとユーザー名による認証

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class EmailorUsernameModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

settings.pyに、次の行を追加します。

AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']

1
from django.contrib.auth.models import User

from django.db import Q

class EmailAuthenticate(object):

    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(email=username) | Q(username=username))
        except User.DoesNotExist:
            return None
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()

        if user.check_password(password):
            return user
        return None

    def get_user(self,user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

そしてsettings.py

AUTHENTICATION_BACKENDS = (
  'articles.backends.EmailAuthenticate',
)

ここで、articlesは私のdjango-app、backends.pyは私のアプリ内のpythonファイル、は私のファイル内EmailAuthenticateの認証バックエンドクラスbackends.pyです。


1

ものすごく単純。追加のクラスは必要ありません。

メールでユーザーを作成および更新するときは、メールでユーザー名フィールドを設定するだけです。

そうすれば、ユーザー名フィールドを認証するときに、電子メールと同じ値になります。

コード:

# Create
User.objects.create_user(username=post_data['email'] etc...)

# Update
user.username = post_data['email']
user.save()

# When you authenticate
user = authenticate(username=post_data['email'], password=password)

1
あなたの答えが問題の解決にどのように役立つかを示すのに役立つサンプルコードをいくつか追加してください。
スーツボーイアプリ

1
@CasmanRidder追加情報を追加しない場合、回答は削除されます。
10担当者は、


0

Django2.xの電子メールによる認証

def admin_login(request):
if request.method == "POST":
    email = request.POST.get('email', None)
    password = request.POST.get('password', None)
    try:
        get_user_name = CustomUser.objects.get(email=email)
        user_logged_in =authenticate(username=get_user_name,password=password)
        if user_logged_in is not None:
            login(request, user_logged_in)
            messages.success(request, f"WelcomeBack{user_logged_in.username}")
            return HttpResponseRedirect(reverse('backend'))
        else:
            messages.error(request, 'Invalid Credentials')
            return HttpResponseRedirect(reverse('admin_login'))
    except:
        messages.warning(request, 'Wrong Email')
        return HttpResponseRedirect(reverse('admin_login'))

else:
    if request.user.is_authenticated:
        return HttpResponseRedirect(reverse('backend'))
    return render(request, 'login_panel/login.html')

あなたの答えが何をするのか、そしてそれが質問に答えるのにどのように役立つのかを説明するために少しテキストを追加できますか?
Jaquez

編集済み。ありがとう
ShakilAhmmed19年

0

カスタムデータベースを作成した場合、そこから電子メールIDとパスワードを検証する必要があります。

  1. メールIDとパスワードを取得する models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2.フェッチされたリストに割り当てる object_query_list

3.リストを文字列に変換する

例:

  1. HTMLEmail_idPassword値をViews.py

    u_email = request.POST.get('uemail')

    u_pass = request.POST.get('upass')

  2. データベースからメールIDとパスワードを取得します

    Email = B_Reg.objects.values_list('B_Email',flat=True).filter(B_Email=u_email)

    Password = B_Reg.objects.values_list('Password',flat=True).filter(B_Email=u_email)

  3. Query設定された値からリスト内の電子メールIDとパスワードの値を取得します

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. リストを文字列に変換する

    string_email = ''.join(map(str, Email_Value))

    string_password = ''.join(map(str, Password_Value))

最後にあなたのログイン条件

if (string_email==u_email and string_password ==u_pass)

0

そのためのヘルパーを作成しました:function authenticate_user(email, password)

from django.contrib.auth.models import User


def authenticate_user(email, password):
    try:
        user = User.objects.get(email=email)
    except User.DoesNotExist:
        return None
    else:
        if user.check_password(password):
            return user

    return None

class LoginView(View):
    template_name = 'myapp/login.html'

    def get(self, request):
        return render(request, self.template_name)

    def post(self, request):
        email = request.POST['email']
        password = request.POST['password']
        user = authenticate_user(email, password)
        context = {}

        if user is not None:
            if user.is_active:
                login(request, user)

                return redirect(self.request.GET.get('next', '/'))
            else:
                context['error_message'] = "user is not active"
        else:
            context['error_message'] = "email or password not correct"

        return render(request, self.template_name, context)

0

これを行う方法はDjango3.0で更新されたようです。

私にとっての作業方法は次のとおりです。

authentication.py#<-これをアプリに配置しました(settings.pyと一緒にプロジェクトフォルダーで機能しませんでした)

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class EmailBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

次に、これをsettings.pyファイルに追加しました

AUTHENTICATION_BACKENDS = (
    'appname.authentication.EmailBackend',
)

0

デフォルトのユーザーモデルは、抽象クラスを継承/拡張します。フレームワークは、ある程度の変更や変更に対して寛容でなければなりません。

より簡単なハックは、次のことを行うことです。これは仮想環境にあります

  1. djangoのインストール場所に移動し、Libを見つけますフォルダーます
  2. django / contrib / auth /に移動します
  3. models.pyファイルを見つけて開きます。AbstractUserクラスの行315を見つけます

電子メール属性の行336は、一意を追加してtrueに設定します

email = models.EmailField(_('email address'), blank=True,unique=True)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
  1. 完了、移行、移行

あなた自身の責任でこれをしてください、

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.