Войдите в систему, используя адрес электронной почты или имя пользователя в Django
Я пытаюсь создать бэкэнд auth, чтобы мои пользователи могли войти в систему, используя свой адрес электронной почты или свое имя пользователя в Django 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
Edit: как было предложено, я унаследовал от ModelBackend и установил его в своих настройках В моих настройках у меня есть это AUTHENTICATION_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 ошибка.
7 ответов
еще одно решение:
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
class DualModelBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
# `username` field does not restring using `@`, so technically email can be
# as username and email, even with different users
users = UserModel._default_manager.filter(
Q(**{UserModel.USERNAME_FIELD: username}) | Q(email__iexact=username))
# check for any password match
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 (#20760).
UserModel().set_password(password)
устранение:
- по умолчанию
@не запрещено в поле Имя пользователя, поэтому, если пользовательская модель пользователя не запрещает@символ, он не может использоваться для различения имени пользователя и электронной почты. - технически, могут быть два пользователя, использующие одно и то же письмо, один в поле электронной почты, другой в имени пользователя. Если такая возможность не ограничена, это может привести к тому, что пользователь не сможет аутентифицироваться или необработанный
MultipleObjectsReturnedисключение, если есть. - ловить любое исключение с
except:это вообще плохая практика
недостаток-если есть два пользователя, используя тот же адрес электронной почты, один в имени пользователя, другой в электронной почте, и они имеют тот же пароль, то он склонен к аутентификации первого матча. Думаю, шансы на это крайне малы.
обратите внимание: любой из подходов должен обеспечивать unique
после следования совету, данному мне выше, и изменения 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
Я думал, что брошу свой более простой подход для всех, кто столкнется с этим:
# -*- 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хотя бы сделать это не)
обновленная версия того же фрагмента, с улучшенной безопасностью. Кроме того, это позволяет включить или отключить проверку подлинности с учетом регистра. Если вы предпочитаете, вы можете установите его непосредственно из 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 auth. Я не видел, чтобы кто-то использовал этот метод, поэтому решил поделиться им для простоты.
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',
)
вот обходной путь, который не требует изменения бэкэнда аутентификации вообще.
во-первых, посмотрите на пример входа в систему от Джанго.
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')