Zaloguj użytkownika używając adresu email lub nazwy użytkownika w Django
Próbuję stworzyć backend auth, aby umożliwić moim użytkownikom logowanie się przy użyciu ich adresu e-mail lub nazwy użytkownika w Django 1.6 z niestandardowym modelem użytkownika. Backend działa, gdy loguję się za pomocą nazwy użytkownika, ale z jakiegoś powodu nie ma wiadomości e-mail. Czy jest coś, o czym zapomniałem zrobić?
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: zgodnie z sugestią odziedziczyłem ModelBackend i zainstalowałem go w moich ustawieniach W moich ustawieniach mam to AUTHENTICATION_BACKENDS = ( "użytkownicy.backends", 'django.contrib.auth.backends.ModelBackend", ) A ja zmieniłem backend na ten:
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
Teraz dostaję Module "users" does not define a "backends" attribute/class
Błąd.
7 answers
Jeszcze jedno rozwiązanie:
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)
Poprawki:
- domyślnie,
@
nie jest zabronione w polu Nazwa użytkownika, więc jeśli niestandardowy model użytkownika nie zabrania symbolu@
, nie może być użyty do rozróżnienia nazwy użytkownika i adresu e-mail. - technicznie, może być dwóch użytkowników korzystających z tego samego e-maila, jeden w polu e-mail, drugi w Nazwie użytkownika. O ile taka możliwość nie jest ograniczona, może to spowodować, że użytkownik nie będzie mógł uwierzytelnić się lub nieobsługiwany wyjątek
MultipleObjectsReturned
, JeśliUserModel._default_manager.get(Q(username__iexact=username) | Q(email__iexact=username))
jest używany. - Łapanie jakiegokolwiek wyjątku z {[5] } jest zazwyczaj złą praktyką
Minus-jeśli jest dwóch użytkowników, używających tego samego e-maila, jeden w Nazwie użytkownika, drugi w e-mailu i mają to samo hasło, to jest podatne na uwierzytelnianie pierwszego dopasowania. Myślę, że szanse na to są bardzo mało prawdopodobne.
Uwaga: każda z metod powinna wymusić pole unique email
w modelu użytkownika, ponieważ domyślny model użytkownika nie definiuje unique e-mail, który prowadziłby do nieobsługiwanego wyjątku w przypadku użycia User.objects.get(email__iexact="...")
, lub uwierzytelniania pierwszego dopasowania. W każdym razie korzystanie z poczty e-mail do logowania zakłada, że e-mail jest unikalny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-16 11:10:13
Po zastosowaniu się do Rady udzielonej mi powyżej i zmianie AUTHENTICATION_BACKENDS = ['yourapp.yourfile.EmailOrUsernameModelBackend']
otrzymałem błąd Manager isn't available; User has been swapped for 'users.User'
. Było to spowodowane tym, że używałem domyślnego modelu użytkownika zamiast własnego niestandardowego. Oto działający kod.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-12 20:19:05
Pomyślałem, że rzucę swoje prostsze podejście dla każdego, kto się na to natknie:
# -*- 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)
Uwaga:
- lekceważenie
USERNAME_FIELD
, choć można by go z łatwością dodać - wielkość liter jest niewrażliwa (możesz po prostu usunąć
__iexact
, aby nie)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 15:20:23
Zaktualizowana wersja tego samego fragmentu, z ulepszonym zabezpieczeniem. Umożliwia również włączanie lub wyłączanie uwierzytelniania uwzględniającego wielkość liter. Jeśli wolisz, możesz zainstalować go bezpośrednio z 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-22 19:59:45
Wiem, że już na to odpowiedziałam, jednak znalazłem naprawdę zgrabny sposób na zaimplementowanie loginu zarówno za pomocą e-maila, jak i nazwy użytkownika przy użyciu widoków Auth Django. Nie widziałem, żeby ktoś używał tego typu metody, więc pomyślałem, że podzielę się nią ze względu na prostotę.
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
Wtedy w Twoim settings.py add this
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'myapp.authentication.EmailAuthBackend',
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 21:51:25
Oto obejście, które w ogóle nie wymaga modyfikacji zaplecza uwierzytelniania.
Najpierw spójrz na przykładowy widok logowania z Django.
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.
...
Jeśli uwierzytelnienie z nazwą użytkownika nie powiedzie się, możemy sprawdzić, czy wiadomość e-mail pasuje, uzyskać odpowiednią nazwę użytkownika i spróbować ponownie uwierzytelnić.
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.
...
Podobnie jak w przykładzie 1bit0fme, email powinien być unikalnym polem i jest ten sam (bardzo mało prawdopodobny) minus, o którym mówili.
I would Poleć to podejście tylko wtedy, gdy wszystkie logowanie na twojej stronie jest obsługiwane przez jeden widok lub formularz. W przeciwnym razie lepiej byłoby zmodyfikować samą metodę authenticate () w backendzie, aby uniknąć tworzenia wielu punktów potencjalnej awarii.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-30 17:31:08
Zakładając, że zablokowałeś / zakazałeś użytkownikowi @ i chcesz użyć modelu użytkownika 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')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-24 02:11:21