web-dev-qa-db-ja.com

Django

ユーザーがDjango 1.6のカスタムユーザーモデルで1.6のメールアドレスまたはユーザー名を使用してログインできるように、認証バックエンドを作成しようとしています。バックエンドは、ユーザー名ですが、何らかの理由でメールが届きません。忘れていることはありますか?

from Django.conf import settings
from Django.contrib.auth.models import User

class EmailOrUsernameModelBackend(object):
    """
    This is a ModelBacked that allows authentication with either a username or an email address.

    """
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = User.objects.get(**kwargs)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

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

編集:提案されたように、ModelBackendを継承し、それを設定にインストールしました。私の設定には、AUTHENTICATION_BACKENDS =( 'users.backends'、 'Django.contrib.auth.backends.ModelBackend'、)があり、バックエンドをこの:

from Django.conf import settings
from Django.contrib.auth.models import User
from Django.contrib.auth.backends import ModelBackend
class EmailOrUsernameModelBackend(ModelBackend):
    """
    This is a ModelBacked that allows authentication with either a username or an email address.

    """
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = User.objects.get(**kwargs)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

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

今私はModule "users" does not define a "backends" attribute/classエラー。

28
user3282276

上記のアドバイスに従ってAUTHENTICATION_BACKENDS = ['yourapp.yourfile.EmailOrUsernameModelBackend']エラーが発生しましたManager isn't available; User has been swapped for 'users.User'。これは、自分のカスタムモデルではなくデフォルトのユーザーモデルを使用していたために発生しました。これが実際のコードです。

from Django.conf import settings
from Django.contrib.auth import get_user_model

class EmailOrUsernameModelBackend(object):
    """
    This is a ModelBacked that allows authentication with either a username or an email address.

    """
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = get_user_model().objects.get(**kwargs)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

    def get_user(self, username):
        try:
            return get_user_model().objects.get(pk=username)
        except get_user_model().DoesNotExist:
            return None
11
user3282276

さらに別の解決策:

_from Django.contrib.auth import get_user_model
from Django.contrib.auth.backends import ModelBackend
from Django.db.models import Q


class EmailOrUsernameModelBackend(ModelBackend):
    """
    Authentication backend which allows users to authenticate using either their
    username or email address

    Source: https://stackoverflow.com/a/35836674/59984
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        # n.b. Django <2.1 does not pass the `request`

        user_model = get_user_model()

        if username is None:
            username = kwargs.get(user_model.USERNAME_FIELD)

        # The `username` field is allows to contain `@` characters so
        # technically a given email address could be present in either field,
        # possibly even for different users, so we'll query for all matching
        # records and test each one.
        users = user_model._default_manager.filter(
            Q(**{user_model.USERNAME_FIELD: username}) | Q(email__iexact=username)
        )

        # Test whether any matched user has the provided password:
        for user in users:
            if user.check_password(password):
                return user
        if not users:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a non-existing user (see
            # https://code.djangoproject.com/ticket/20760)
            user_model().set_password(password)
_

修正:

  • デフォルトでは、ユーザー名フィールドで_@_は禁止されていないため、カスタムユーザーモデルが_@_記号を禁止しない限り、ユーザー名とメールの区別に使用できません。
  • 技術的には、同じメールを使用する2人のユーザーが存在する可能性があります。そのような可能性が制限されていない限り、ユーザーが認証できないか、UserModel._default_manager.get(Q(username__iexact=username) | Q(email__iexact=username))が使用されている場合は未処理のMultipleObjectsReturned例外が発生する可能性があります。
  • _except:_を使用して例外をキャッチすることは、一般的に悪い習慣です

欠点-2人のユーザーが同じ電子メールを使用していて、1人はユーザー名でもう1人は電子メールで、同じパスワードを持っている場合、最初の一致を認証する傾向があります。これの可能性は非常に低いと思います。

また、注:デフォルトのユーザーモデルでは一意のメールが定義されていないため、どのモデルでも一意のemailフィールドを強制する必要があります。これにより、User.objects.get(email__iexact="...")の場合に未処理の例外が発生します。が使用されているか、最初の一致を認証しています。いずれの場合も、ログインに電子メールを使用する場合、電子メールは一意であると見なされます。

18
1bit0fMe

私はこれに遭遇した他の人のために私のより簡単なアプローチをやめようと思いました:

# -*- coding: utf-8 -*-
from Django.contrib.auth import backends, get_user_model
from Django.db.models import Q


class ModelBackend(backends.ModelBackend):
    def authenticate(self, username=None, password=None, **kwargs):
        UserModel = get_user_model()

        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))

            if user.check_password(password):
                return user
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a non-existing user (#20760).
            UserModel().set_password(password)

注意:

  • を無視するUSERNAME_FIELD、かなり簡単に元に戻すことができますが
  • 大文字と小文字を区別しません(__iexactは、そうしないことを意図しています)
6
Steve

私はこれがすでに回答されていることを知っていますが、Django auth viewを使用して、電子メールとユーザー名の両方でログインを実装するための本当にきちんとした方法を見つけました。簡単にするために共有したいと思いました。

from Django.contrib.auth.models import User


class EmailAuthBackend():
    def authenticate(self, username=None, password=None):
        try:
            user = User.objects.get(email=username)
            if user.check_password(raw_password=password):
                return user
            return None
        except User.DoesNotExist:
            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 = (
    'Django.contrib.auth.backends.ModelBackend',
    'myapp.authentication.EmailAuthBackend',
)
4
ex8

セキュリティが強化された、同じスニペットの更新バージョン。また、大文字と小文字を区別する認証を有効または無効にできます。必要に応じて、 pypiから直接インストール できます。

from Django.contrib.auth.backends import ModelBackend
from Django.contrib.auth import get_user_model
from Django.conf import settings

###################################
"""  DEFAULT SETTINGS + ALIAS   """
###################################


try:
    am = settings.AUTHENTICATION_METHOD
except:
    am = 'both'
try:
    cs = settings.AUTHENTICATION_CASE_SENSITIVE
except:
    cs = 'both'

#####################
"""   EXCEPTIONS  """
#####################


VALID_AM = ['username', 'email', 'both']
VALID_CS = ['username', 'email', 'both', 'none']

if (am not in VALID_AM):
    raise Exception("Invalid value for AUTHENTICATION_METHOD in project "
                    "settings. Use 'username','email', or 'both'.")

if (cs not in VALID_CS):
    raise Exception("Invalid value for AUTHENTICATION_CASE_SENSITIVE in project "
                    "settings. Use 'username','email', 'both' or 'none'.")

############################
"""  OVERRIDDEN METHODS  """
############################


class DualAuthentication(ModelBackend):
    """
    This is a ModelBacked that allows authentication
    with either a username or an email address.
    """

    def authenticate(self, username=None, password=None):
        UserModel = get_user_model()
        try:
            if ((am == 'email') or (am == 'both')):
                if ((cs == 'email') or cs == 'both'):
                    kwargs = {'email': username}
                else:
                    kwargs = {'email__iexact': username}

                user = UserModel.objects.get(**kwargs)
            else:
                raise
        except:
            if ((am == 'username') or (am == 'both')):
                if ((cs == 'username') or cs == 'both'):
                    kwargs = {'username': username}
                else:
                kwargs = {'username__iexact': username}

                user = UserModel.objects.get(**kwargs)
        finally:
            try:
                if user.check_password(password):
                    return user
            except:
                # Run the default password hasher once to reduce the timing
                # difference between an existing and a non-existing user.
                UserModel().set_password(password)
                return None

    def get_user(self, username):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=username)
        except UserModel.DoesNotExist:
            return None
2
Adrian Lopez

django-rest-authを使用している場合は、メールアドレスで認証するオプションが組み込まれており、提案されている他の方法と競合する可能性があります。 settings.pyに以下を追加するだけです:

ACCOUNT_AUTHENTICATION_METHOD = 'username_email'
ACCOUNT_EMAIL_REQUIRED = False
ACCOUNT_USERNAME_REQUIRED = False

#Following is added to enable registration with email instead of username
AUTHENTICATION_BACKENDS = (
 # Needed to login by username in Django admin, regardless of `allauth`
 "Django.contrib.auth.backends.ModelBackend",

 # `allauth` specific authentication methods, such as login by e-mail
 "allauth.account.auth_backends.AuthenticationBackend",
)

ユーザー名の代わりにDjango REST認証メールhttps://Django-allauth.readthedocs.io/en/latest/configuration.html

ユーザーがユーザー名またはメールアドレスを入力できる単一のボックスが必要でない限り、フロントエンドでいくつかの作業を行って、ログインリクエストをメール、パスワード、またはユーザー名のどちらで送信するかを決定する必要があります。パスワード。ユーザーのエントリに「。」付きの「@」が含まれているかどうかを簡単にテストしました。これより先。電子メールアドレスのように見えるユーザー名を故意に作成したが、その電子メールアドレスではない場合、私がそれをサポートしていない可能性は低いと思います。

1
Little Brain

このためのコードを2つの簡単なステップで記述しました。

  1. VIEWS.py
     if request.method == 'POST':
            userinput = request.POST['username']

            try:
                username = userbase.objects.get(email=userinput).username
            except userbase.DoesNotExist:
                username = request.POST['username']
            password = request.POST['password']
  1. INDEX.html

    2つの入力フィールドを作成しました。1つ目はユーザー名/メール用です。与えられた入力をすべて取り、dbのemail列で同じデータを検索しようとします。一致する場合はユーザー名を返し、認証を試みます。一致しない場合は、入力を直接ユーザー名として使用します。

Django 2.2を使用しています

1
krishna lodha

私は自分の考えでソリューションを実装しました。ただし、私のユーザーは、登録時にユーザー名としてメールを使用することを許可されていません。また、各メールは一意です。

if request.method=="POST":  
    username = request.POST.get('username').lower()
    password = request.POST.get('password')
    '''check if the username is a valid email'''
    try:
        email = validate_email(username)
        username = User.objects.get(email=username).username
    except:
        pass
    user = authenticate(request, username=username, password=password)
    if user is not None:
        login(request,user)
    else:
        messages.error(request,("Error logging in."))
        return redirect('login')

ユーザーのユーザー名に@を含めることができるように、validate_emailを使用しています。@ bestuserは有効なユーザー名ですが、有効なメールではありません。私にとってはうまくいき、認証方法を上書きする必要はありません。

0
Robert Mutua

これらのようなほとんどのソリューションでは、認証の脆弱性を回避するために、ユーザーモデルに電子メールの一意性検証を追加する必要があります。

# models.py
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'

次に、AUTH_USER_MODELプロパティを定義するsettings.pyを更新する必要があります

AUTH_USER_MODEL = '[your_app_name].User'
0
dtar

認証バックエンドをまったく変更する必要がない回避策を次に示します。

まず、Djangoの example login view を見てください。

from Django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        login(request, user)
        # Redirect to a success page.
        ...
    else:
        # Return an 'invalid login' error message.
        ...

ユーザー名による認証が失敗した場合、メールが一致するかどうかを確認し、対応するユーザー名を取得して、再度認証を試みます。

from Django.contrib.auth import authenticate, login, get_user_model

def my_view(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is None:
        User = get_user_model()
        user_queryset = User.objects.all().filter(email__iexact=username)
        if user_queryset:
            username = user_queryset[0].username
            user = authenticate(username=username, password=password)
    if user is not None:
        login(request, user)
        # Redirect to a success page.
        ...
    else:
        # Return an 'invalid login' error message.
        ...

1bit0fMeの例と同様に、電子メールは一意のフィールドである必要があり、彼らが言及したのと同じ(ほとんどない)欠点があります。

このアプローチは、サイトへのすべてのログインが単一のビューまたはフォームで処理される場合にのみお勧めします。それ以外の場合は、バックエンドでauthenticate()メソッド自体を変更して、潜在的な障害の複数のポイントを作成しないようにすることをお勧めします。

0
John Meinken

@が付いたユーザー名に対してブロック/禁止されており、Djangoユーザーモデルを使用したいとします。

if request.method == 'POST':
    form = LoginForm(request.POST)
    if form.is_valid():
        cd=form.cleaned_data
        if '@' in cd['username']:
            username=User.objects.get(email=cd['username']).username
        else:
            username=cd['username']

        user = authenticate(username=username,
                                password=cd['password'])

        if user is not None and user.is_active:
            login(request,user)
            return redirect('loggedin')
        else:
            return render(request, 'login.html')
0