Django - Zaloguj się przez e-mail

86

Chcę, aby django uwierzytelniało użytkowników przez e-mail, a nie przez nazwy użytkowników. Jednym ze sposobów może być podanie wartości e-mail jako wartości nazwy użytkownika, ale ja tego nie chcę. Powodem jest to, że mam adres URL /profile/<username>/, dlatego nie mogę mieć adresu URL /profile/[email protected]/.

Innym powodem jest to, że wszystkie e-maile są unikalne, ale czasami zdarza się, że nazwa użytkownika jest już zajęta. Dlatego automatycznie tworzę nazwę użytkownika jako fullName_ID.

Jak mogę po prostu zmienić zezwolenie Django na uwierzytelnianie za pomocą poczty e-mail?

W ten sposób tworzę użytkownika.

username = `abcd28`
user_email = `[email protected]`
user = User.objects.create_user(username, user_email, user_pass)

Tak się loguję.

email = request.POST['email']
password = request.POST['password']
username = User.objects.get(email=email.lower()).username
user = authenticate(username=username, password=password)
login(request, user)

Czy istnieje inny sposób logowania oprócz uzyskania nazwy użytkownika w pierwszej kolejności?

PythonEnthusiast
źródło

Odpowiedzi:

109

Powinieneś napisać niestandardowe zaplecze uwierzytelniania. Coś takiego zadziała:

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

Następnie ustaw ten backend jako backend uwierzytelniania w ustawieniach:

AUTHENTICATION_BACKENDS = ['path.to.auth.module.EmailBackend']

Zaktualizowano . Dziedzicz z, ModelBackendponieważ implementuje metody takie jak get_user()już.

Zobacz dokumenty tutaj: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend

mipadi
źródło
6
Używając django 1.9.8 mam błąd: Obiekt „EmailBackend” nie ma atrybutu „get_user”. Rozwiązane przez dodanie metody `` get_user '' zgodnie z tym stackoverflow.com/a/13954358/2647009
baltasvejas
1
Proszę określić, dla której wersji Django ten kod może działać. Niektórzy narzekają na brak metody get_user.
Dr. Younes Henni
2
Zamiast po prostu if user.check_password(password):prawdopodobnie chcesz to co robi Django domyślnie poprzez ModelBackend: if user.check_password(password) and self.user_can_authenticate(user):w celu sprawdzenia, czy użytkownik ma is_active=True.
jmq
1
Czy to nie jest podatne na atak czasowy, ponieważ nie obejmuje ograniczenia Django w kodzie źródłowym?
Gabriel Garcia
Teraz treść żądania powinna zawierać pola, takie jak nazwa użytkownika i hasło. Czy jest jakiś sposób, aby zmienić to na e-mail i hasło?
Karol
55

Jeśli zaczynasz nowy projekt, django zdecydowanie zaleca skonfigurowanie niestandardowego modelu użytkownika. (patrz https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project )

a jeśli to zrobiłeś, dodaj trzy linie do swojego modelu użytkownika:

class MyUser(AbstractUser):
    USERNAME_FIELD = 'email'
    email = models.EmailField(_('email address'), unique=True) # changes email to unique and blank to false
    REQUIRED_FIELDS = [] # removes email from REQUIRED_FIELDS

Wtedy authenticate(email=email, password=password)działa, a authenticate(username=username, password=password)przestaje działać.

Mikołaja
źródło
3
Podczas uruchamiania createuperuser, to samo zgłasza błąd: TypeError: create_superuser () brakuje 1 wymaganego argumentu pozycyjnego: „nazwa użytkownika”. Musisz użyć niestandardowego menedżera użytkowników: class MyUserManager(BaseUserManager): def create_superuser(self, email, password, **kwargs): user = self.model(email=email, is_staff=True, is_superuser=True, **kwargs) user.set_password(password) user.save() return user
Michał Holub
18
Pełne instrukcje tutaj: fomfus.com/articles/…
user2061057
1
Potem znowu, docs Django odradzamy przy użyciu niestandardowego modelu użytkownika, jeśli tworzysz wielokrotnego użytku aplikacji.
djvg,
14

Uwierzytelnianie poczty elektronicznej dla Django 3.x

Aby użyć adresu e-mail / nazwy użytkownika i hasła do uwierzytelniania zamiast domyślnej nazwy użytkownika i hasła, musimy zastąpić dwie metody klasy ModelBackend: autenticate () i get_user ():

Metoda get_user przyjmuje identyfikator_użytkownika - który może być nazwą użytkownika, identyfikatorem bazy danych lub czymkolwiek, ale musi być unikalny dla obiektu użytkownika - i zwraca obiekt użytkownika lub None. Jeśli nie zachowałeś e-maila jako unikalnego klucza, będziesz musiał zadbać o wiele wyników zwracanych dla query_set. W poniższym kodzie zadbano o to, zwracając pierwszego użytkownika ze zwróconej listy.

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try: #to allow authentication through phone number or any other field, modify the below statement
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

        return user if self.user_can_authenticate(user) else None

Domyślnie AUTHENTICATION_BACKENDS jest ustawione na:

['django.contrib.auth.backends.ModelBackend']

W pliku settings.py dodaj następujący u dołu, aby zastąpić ustawienie domyślne:

AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)
Himanshu Singh
źródło
To świetnie, dzięki. Ukończyłem projekt, szablony, formularze, widoki, dużo, więc zaczynanie od nowa nie jest takie atrakcyjne! Teraz mogę uwierzytelnić się na adresie e-mail. Czy istnieje sposób na usunięcie pola nazwy użytkownika, aby nie było uwzględnione w uwierzytelnianiu i formularzach renderowanych w szablonach?
moonraker,
11

Miałem podobny wymóg, w którym nazwa użytkownika / adres e-mail powinny działać w polu nazwy użytkownika.Jeśli ktoś szuka sposobu uwierzytelniania, aby to zrobić, sprawdź następujący kod roboczy.Możesz zmienić zestaw zapytań, jeśli chcesz tylko e-mail.

from django.contrib.auth import get_user_model  # gets the user_model django  default or your own custom
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q


# Class to permit the athentication using email or username
class CustomBackend(ModelBackend):  # requires to define two functions authenticate and get_user

    def authenticate(self, username=None, password=None, **kwargs):
        UserModel = get_user_model()

        try:
            # below line gives query set,you can change the queryset as per your requirement
            user = UserModel.objects.filter(
                Q(username__iexact=username) |
                Q(email__iexact=username)
            ).distinct()

        except UserModel.DoesNotExist:
            return None

        if user.exists():
            ''' get the user object from the underlying query set,
            there will only be one object since username and email
            should be unique fields in your models.'''
            user_obj = user.first()
            if user_obj.check_password(password):
                return user_obj
            return None
        else:
            return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

Dodaj również AUTHENTICATION_BACKENDS = ('path.to.CustomBackend',) w settings.py

anuragb26
źródło
To działało dla mnie, dopóki nie zaktualizowałem wersji 1.11 do 2.1.5. Masz jakiś pomysł, dlaczego nie będzie działać w tej wersji?
zerohedge
@zerohedge dodaj żądanie do parametrów metody uwierzytelniania. Zobacz docs.djangoproject.com/en/2.2/topics/auth/customizing/…
Van
Pozostawia cię również otwartym na atak czasowy, warto spróbować dokładnie naśladować implementację Django, aby zapobiec takim lukom
Gabriel Garcia
Umożliwi to również uwierzytelnianie nieaktywnym użytkownikom.
Gabriel Garcia
5

Django 2.x

Jak wspomniano powyżej przez Ganesh dla django 2.x, metoda uwierzytelniania wymaga teraz parametru żądania.

# backends.py
from django.contrib.auth import backends, get_user_model
from django.db.models import Q
UserModel = get_user_model()


class ModelBackend(backends.ModelBackend):

    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        try:
            # user = UserModel._default_manager.get_by_natural_key(username)
            # You can customise what the given username is checked against, here I compare to both username and email fields of the User model
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user
        return super().authenticate(request, username, password, **kwargs)

dodaj backend do ustawień projektu

# settings.py
AUTHENTICATION_BACKENDS = ['path.to.ModelBackend']

Twój niestandardowy model użytkownika będzie musiał tworzyć unikalne wiadomości e-mail dla aktywnych i zweryfikowanych użytkowników. Możesz to zrobić za pomocą czegoś takiego:

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'

Aby jednak uniemożliwić komuś blokowanie korzystania z adresu e-mail przez inną osobę, należy zamiast tego dodać weryfikację adresu e-mail, a proces rejestracji i logowania wziąć pod uwagę, że wiadomości e-mail mogą nie być unikalne (i prawdopodobnie uniemożliwić nowym użytkownikom korzystanie z istniejącego i zweryfikowanego adresu e-mail).

pion
źródło
4

Uwierzytelnianie adresu e-mail i nazwy użytkownika dla Django 2.X

Mając na uwadze, że jest to częste pytanie, oto niestandardowa implementacja naśladująca kod źródłowy Django, ale która uwierzytelnia użytkownika za pomocą nazwy użytkownika lub adresu e-mail, bez rozróżniania wielkości liter, zachowując ochronę przed atakami czasowymi i nie uwierzytelniając nieaktywnych użytkowników .

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

        return user if self.user_can_authenticate(user) else None

Zawsze pamiętaj, aby dodać to swoje settings.py do właściwego zaplecza uwierzytelniania .

Gabriel Garcia
źródło
1
Czy dobrze rozumiem, że UserModel().set_password(password)ma to na celu uniemożliwić atakującym określenie, czy użytkownik istnieje, czy nie, wykonując mniej więcej taką samą ilość pracy kryptograficznej, niezależnie (zakładam, że jest to atak czasowy, o którym mowa)?
Grand Phuba
2

Należy dostosować klasę ModelBackend. Mój prosty kod:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

class YourBackend(ModelBackend):

  def authenticate(self, username=None, password=None, **kwargs):
    UserModel = get_user_model()
    if username is None:
        username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
        if '@' in username:
            UserModel.USERNAME_FIELD = 'email'
        else:
            UserModel.USERNAME_FIELD = 'username'

        user = UserModel._default_manager.get_by_natural_key(username)
    except UserModel.DoesNotExist:
        UserModel().set_password(password)
    else:
        if user.check_password(password) and self.user_can_authenticate(user):
            return user

A w pliku settings.py dodaj:

AUTHENTICATION_BACKENDS = ['path.to.class.YourBackend']
Hùng Ng Vi
źródło
Zaktualizuj swój kod, aby uwzględnić requestparametr w authenticatemetodzie dla django 2.1.1
Ganesh
2

Uwierzytelnianie za pomocą adresu e-mail i nazwy użytkownika Dla Django 2.x

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class EmailorUsernameModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

W settings.py dodaj następujący wiersz,

AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']
Chetan
źródło
1
from django.contrib.auth.models import User

from django.db import Q

class EmailAuthenticate(object):

    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(email=username) | Q(username=username))
        except User.DoesNotExist:
            return None
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()

        if user.check_password(password):
            return user
        return None

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

A potem w settings.py:

AUTHENTICATION_BACKENDS = (
  'articles.backends.EmailAuthenticate',
)

gdzie artykuły to moja aplikacja django, backends.pyto plik Pythona w mojej aplikacji i EmailAuthenticateklasa zaplecza uwierzytelniania w moim backends.pypliku

Animesh Timsina
źródło
1

Dość proste. Nie ma potrzeby prowadzenia dodatkowych zajęć.

Podczas tworzenia i aktualizowania użytkownika za pomocą adresu e-mail, po prostu ustaw pole nazwy użytkownika na adres e-mail.

W ten sposób podczas uwierzytelniania pole nazwy użytkownika będzie miało tę samą wartość co e-mail.

Kod:

# Create
User.objects.create_user(username=post_data['email'] etc...)

# Update
user.username = post_data['email']
user.save()

# When you authenticate
user = authenticate(username=post_data['email'], password=password)
Casman Ridder
źródło
1
Dodaj przykładowy kod, aby zademonstrować, w jaki sposób Twoja odpowiedź może pomóc w rozwiązaniu problemu.
Suit Boy Apps,
1
@CasmanRidder Twoja odpowiedź zostanie usunięta, jeśli nie dodasz dodatkowych informacji.
10 przedstawicieli mówi zaszczepić się
0

W przypadku Django 2

username = get_object_or_404(User, email=data["email"]).username
        user = authenticate(
            request, 
            username = username, 
            password = data["password"]
        )
        login(request, user)
Juan Sebastian Parada Celis
źródło
0

Uwierzytelnianie za pomocą poczty elektronicznej Dla Django 2.x

def admin_login(request):
if request.method == "POST":
    email = request.POST.get('email', None)
    password = request.POST.get('password', None)
    try:
        get_user_name = CustomUser.objects.get(email=email)
        user_logged_in =authenticate(username=get_user_name,password=password)
        if user_logged_in is not None:
            login(request, user_logged_in)
            messages.success(request, f"WelcomeBack{user_logged_in.username}")
            return HttpResponseRedirect(reverse('backend'))
        else:
            messages.error(request, 'Invalid Credentials')
            return HttpResponseRedirect(reverse('admin_login'))
    except:
        messages.warning(request, 'Wrong Email')
        return HttpResponseRedirect(reverse('admin_login'))

else:
    if request.user.is_authenticated:
        return HttpResponseRedirect(reverse('backend'))
    return render(request, 'login_panel/login.html')
Shakil Ahmmed
źródło
Czy możesz dodać trochę tekstu, aby wyjaśnić, co robi twoja odpowiedź i jak pomaga odpowiedzieć na pytanie?
Jaquez
Edytowano. Dzięki
Shakil Ahmmed
0

Jeśli utworzyłeś niestandardową bazę danych, stamtąd, jeśli chcesz zweryfikować swój identyfikator e-mail i hasło.

  1. Pobierz identyfikator e-mail i hasło za pomocą models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2. przypisanie na liście zostało pobrane object_query_list

3. Konwertuj listę na ciąg

Np .:

  1. Weź kod HTML Email_idi PasswordwartościViews.py

    u_email = request.POST.get('uemail')

    u_pass = request.POST.get('upass')

  2. Pobierz identyfikator e-mail i hasło z bazy danych

    Email = B_Reg.objects.values_list('B_Email',flat=True).filter(B_Email=u_email)

    Password = B_Reg.objects.values_list('Password',flat=True).filter(B_Email=u_email)

  3. Weź identyfikator e-mail i wartości hasła z listy z Queryzestawu wartości

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. Konwertuj listę na ciąg

    string_email = ''.join(map(str, Email_Value))

    string_password = ''.join(map(str, Password_Value))

Wreszcie twój warunek logowania

if (string_email==u_email and string_password ==u_pass)
sagar rathod
źródło
0

Stworzyłem do tego pomocnika: funkcję authenticate_user(email, password).

from django.contrib.auth.models import User


def authenticate_user(email, password):
    try:
        user = User.objects.get(email=email)
    except User.DoesNotExist:
        return None
    else:
        if user.check_password(password):
            return user

    return None

class LoginView(View):
    template_name = 'myapp/login.html'

    def get(self, request):
        return render(request, self.template_name)

    def post(self, request):
        email = request.POST['email']
        password = request.POST['password']
        user = authenticate_user(email, password)
        context = {}

        if user is not None:
            if user.is_active:
                login(request, user)

                return redirect(self.request.GET.get('next', '/'))
            else:
                context['error_message'] = "user is not active"
        else:
            context['error_message'] = "email or password not correct"

        return render(request, self.template_name, context)
Wariored
źródło
0

Wygląda na to, że metoda robienia tego została zaktualizowana w Django 3.0.

Metodą pracy jest dla mnie:

authentication.py # <- umieściłem to w aplikacji (nie działało w folderze projektu obok settings.py

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class EmailBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

Następnie dodałem to do pliku settings.py

AUTHENTICATION_BACKENDS = (
    'appname.authentication.EmailBackend',
)
bitFez
źródło
0

Domyślny model użytkownika dziedziczy / rozszerza klasę abstrakcyjną. Ramy powinny być łagodne dla pewnej liczby zmian lub zmian.

Prostszym sposobem jest wykonanie następujących czynności: To jest w środowisku wirtualnym

  1. Przejdź do lokalizacji instalacji django i znajdź plik Lib folder
  2. przejdź do django / contrib / auth /
  3. znajdź i otwórz plik models.py. Znajdź wiersz klasy AbstractUser 315

LINIA 336 w atrybucie e-mail dodaj unikalny i ustaw ją na wartość true

email = models.EmailField(_('email address'), blank=True,unique=True)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
  1. Gotowe, makemigracje i migracja

Robisz to na własne ryzyko,

Ronald Ngwenya
źródło