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.

Author: user3282276, 2014-08-15

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śli UserModel._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.

 9
Author: 1bit0fMe,
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
 8
Author: user3282276,
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)
 3
Author: Steve,
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
 2
Author: Adrian Lopez,
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',
)
 1
Author: ex8,
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.

 0
Author: John Meinken,
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')
 0
Author: George Battaglia,
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