ユーザーが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
エラー。
上記のアドバイスに従って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
さらに別の解決策:
_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)
_
修正:
@
_は禁止されていないため、カスタムユーザーモデルが_@
_記号を禁止しない限り、ユーザー名とメールの区別に使用できません。UserModel._default_manager.get(Q(username__iexact=username) | Q(email__iexact=username))
が使用されている場合は未処理のMultipleObjectsReturned
例外が発生する可能性があります。except:
_を使用して例外をキャッチすることは、一般的に悪い習慣です欠点-2人のユーザーが同じ電子メールを使用していて、1人はユーザー名でもう1人は電子メールで、同じパスワードを持っている場合、最初の一致を認証する傾向があります。これの可能性は非常に低いと思います。
また、注:デフォルトのユーザーモデルでは一意のメールが定義されていないため、どのモデルでも一意のemail
フィールドを強制する必要があります。これにより、User.objects.get(email__iexact="...")
の場合に未処理の例外が発生します。が使用されているか、最初の一致を認証しています。いずれの場合も、ログインに電子メールを使用する場合、電子メールは一意であると見なされます。
私はこれに遭遇した他の人のために私のより簡単なアプローチをやめようと思いました:
# -*- 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
は、そうしないことを意図しています)私はこれがすでに回答されていることを知っていますが、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',
)
セキュリティが強化された、同じスニペットの更新バージョン。また、大文字と小文字を区別する認証を有効または無効にできます。必要に応じて、 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
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
ユーザーがユーザー名またはメールアドレスを入力できる単一のボックスが必要でない限り、フロントエンドでいくつかの作業を行って、ログインリクエストをメール、パスワード、またはユーザー名のどちらで送信するかを決定する必要があります。パスワード。ユーザーのエントリに「。」付きの「@」が含まれているかどうかを簡単にテストしました。これより先。電子メールアドレスのように見えるユーザー名を故意に作成したが、その電子メールアドレスではない場合、私がそれをサポートしていない可能性は低いと思います。
このためのコードを2つの簡単なステップで記述しました。
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']
INDEX.html
2つの入力フィールドを作成しました。1つ目はユーザー名/メール用です。与えられた入力をすべて取り、dbのemail列で同じデータを検索しようとします。一致する場合はユーザー名を返し、認証を試みます。一致しない場合は、入力を直接ユーザー名として使用します。
Django 2.2を使用しています
私は自分の考えでソリューションを実装しました。ただし、私のユーザーは、登録時にユーザー名としてメールを使用することを許可されていません。また、各メールは一意です。
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は有効なユーザー名ですが、有効なメールではありません。私にとってはうまくいき、認証方法を上書きする必要はありません。
これらのようなほとんどのソリューションでは、認証の脆弱性を回避するために、ユーザーモデルに電子メールの一意性検証を追加する必要があります。
# 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'
認証バックエンドをまったく変更する必要がない回避策を次に示します。
まず、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()メソッド自体を変更して、潜在的な障害の複数のポイントを作成しないようにすることをお勧めします。
@が付いたユーザー名に対してブロック/禁止されており、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')