Django - Login mit E-Mail


84

Ich möchte, dass Django Benutzer per E-Mail und nicht über Benutzernamen authentifiziert. Eine Möglichkeit kann darin bestehen, einen E-Mail-Wert als Benutzernamenwert anzugeben, aber das möchte ich nicht. Grund dafür ist, dass ich eine URL habe /profile/<username>/, daher kann ich keine URL haben /profile/abcd@gmail.com/.

Ein weiterer Grund ist, dass alle E-Mails eindeutig sind, aber manchmal wird der Benutzername bereits vergeben. Daher erstelle ich den Benutzernamen automatisch als fullName_ID.

Wie kann ich einfach ändern, damit sich Django per E-Mail authentifiziert?

So erstelle ich einen Benutzer.

username = `abcd28`
user_email = `abcd@gmail.com`
user = User.objects.create_user(username, user_email, user_pass)

So logge ich mich ein.

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)

Gibt es eine andere Möglichkeit, sich anzumelden, als zuerst den Benutzernamen zu erhalten?

Antworten:


104

Sie sollten ein benutzerdefiniertes Authentifizierungs-Backend schreiben. So etwas wird funktionieren:

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

Legen Sie dann dieses Backend in Ihren Einstellungen als Auth-Backend fest:

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

Aktualisiert . Erben Sie von, ModelBackendwie es Methoden wie get_user()bereits implementiert .

Siehe Dokumente hier: https://docs.djangoproject.com/de/3.0/topics/auth/customizing/#writing-an-authentication-backend


6
Bei Verwendung von django 1.9.8 ist ein Fehler aufgetreten: Das Objekt 'EmailBackend' hat kein Attribut 'get_user'. Gelöst durch Hinzufügen der Methode 'get_user' gemäß diesem stackoverflow.com/a/13954358/2647009
baltasvejas

Bitte geben Sie an, für welche Django-Version dieser Code funktionieren kann. Einige beschweren sich über das Fehlen der Methode get_user.
Dr. Younes Henni

2
Anstatt nur zu if user.check_password(password):möchten, möchten Sie wahrscheinlich angeben, was Django standardmäßig über ModelBackend: tut, if user.check_password(password) and self.user_can_authenticate(user):um zu überprüfen, ob der Benutzer dies hat is_active=True.
jmq

1
Ist dies nicht anfällig für einen Timing-Angriff, da die Django-Minderung im Quellcode nicht enthalten ist?
Gabriel Garcia

Der Text einer Anfrage sollte nun Felder wie einen Benutzernamen und ein Passwort enthalten. Gibt es eine Möglichkeit, es in eine E-Mail und ein Passwort zu ändern?
Karol

55

Wenn Sie ein neues Projekt starten, hat django Ihnen dringend empfohlen, ein benutzerdefiniertes Benutzermodell einzurichten. (Siehe https://docs.djangoproject.com/de/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project )

Wenn Sie dies getan haben, fügen Sie Ihrem Benutzermodell drei Zeilen hinzu:

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

Funktioniert dann authenticate(email=email, password=password), während es nicht mehr authenticate(username=username, password=password)funktioniert.


3
Beim Ausführen von createduperuser wird ein Fehler ausgegeben: TypeError: create_superuser () fehlt 1 erforderliches Positionsargument: 'Benutzername'. Sie müssen benutzerdefinierten Benutzer-Manager verwenden: 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
Michal Holub

17
Vollständige Anweisungen hier: fomfus.com/articles/…
user2061057

1
Andererseits raten die Django-Dokumente davon ab , ein benutzerdefiniertes Benutzermodell zu verwenden, wenn Sie eine wiederverwendbare App erstellen .
DJVG

11

Ich hatte eine ähnliche Anforderung, bei der entweder Benutzername / E-Mail für das Feld Benutzername funktionieren sollte. Wenn jemand nach der Authentifizierungs-Backend-Methode sucht, lesen Sie den folgenden Arbeitscode. Sie können den Abfragesatz ändern, wenn Sie nur die E-Mail wünschen.

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

Fügen Sie außerdem AUTHENTICATION_BACKENDS = ('path.to.CustomBackend',) in settings.py hinzu


Dies funktionierte für mich, bis ich ein Upgrade von 1.11 auf 2.1.5 durchführte. Irgendeine Idee, warum es mit dieser Version nicht funktioniert?
Zerohedge

@zerohedge Anforderung zu den Parametern der Authentifizierungsmethode hinzufügen. Siehe docs.djangoproject.com/de/2.2/topics/auth/customizing/…
Van

Es lässt Sie auch offen für einen Timing-Angriff. Es lohnt sich, die Django-Implementierung genau nachzuahmen, um solche Sicherheitslücken zu vermeiden: github.com/django/django/blob/master/django/contrib/auth/…
Gabriel Garcia

Dadurch können sich auch inaktive Benutzer authentifizieren.
Gabriel Garcia

11

E-Mail-Authentifizierung für Django 3.x.

Um E-Mail / Benutzername und Kennwort für die Authentifizierung anstelle der Standardauthentifizierung für Benutzername und Kennwort zu verwenden, müssen zwei Methoden der ModelBackend-Klasse überschrieben werden: authenticate () und get_user ():

Die Methode get_user verwendet eine Benutzer-ID, die ein Benutzername, eine Datenbank-ID oder was auch immer sein kann, aber für Ihr Benutzerobjekt eindeutig sein muss, und gibt ein Benutzerobjekt oder Keine zurück. Wenn Sie E-Mail nicht als eindeutigen Schlüssel gespeichert haben, müssen Sie sich um mehrere Ergebnisse kümmern, die für das query_set zurückgegeben werden. Im folgenden Code wurde dies behoben, indem der erste Benutzer aus der zurückgegebenen Liste zurückgegeben wurde.

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

Standardmäßig ist AUTHENTICATION_BACKENDS auf Folgendes festgelegt:

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

Fügen Sie in der Datei settings.py unten Folgendes hinzu, um die Standardeinstellung zu überschreiben:

AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)

5

Django 2.x.

Wie von Ganesh oben für django 2.x erwähnt, erfordert die Authentifizierungsmethode jetzt einen Anforderungsparameter.

# 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)

Fügen Sie Ihr Backend zu Ihren Projekteinstellungen hinzu

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

Ihr benutzerdefiniertes Benutzermodell muss E-Mails für aktive und validierte Benutzer eindeutig machen. Sie können dies einfach mit folgenden Schritten tun:

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'

Um jedoch zu verhindern, dass jemand andere daran hindert, seine E-Mails zu verwenden, sollten Sie stattdessen eine E-Mail-Validierung hinzufügen und bei Ihrem Registrierungs- und Anmeldevorgang berücksichtigen, dass E-Mails möglicherweise nicht eindeutig sind (und wahrscheinlich verhindern, dass neue Benutzer eine vorhandene und validierte E-Mail-Adresse verwenden).


4

E-Mail- und Benutzernamenauthentifizierung für Django 2.X.

In Anbetracht dessen, dass dies eine häufig gestellte Frage ist, handelt es sich um eine benutzerdefinierte Implementierung, die den Django-Quellcode nachahmt , den Benutzer jedoch entweder mit Benutzername oder E-Mail authentifiziert, wobei die Groß- und Kleinschreibung nicht berücksichtigt wird, der Schutz vor Timing-Angriffen beibehalten wird und inaktive Benutzer nicht authentifiziert werden .

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

Denken Sie immer daran, Ihre Einstellungen hinzuzufügen. Geben Sie das richtige Authentifizierungs-Backend ein .


1
Ist mein Verständnis richtig, UserModel().set_password(password)dass Angreifer verhindern können, dass festgestellt wird, ob ein Benutzer existiert oder nicht, indem sie unabhängig davon ungefähr die gleiche Menge an kryptografischer Arbeit ausführen (ich gehe davon aus, dass dies der von Ihnen beabsichtigte Timing-Angriff ist)?
Grand Phuba

1
@GrandPhuba Du hast vollkommen recht
Gabriel Garcia

2

Sie sollten die ModelBackend-Klasse anpassen. Mein einfacher Code:

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

Fügen Sie in der Datei settings.py Folgendes hinzu:

AUTHENTICATION_BACKENDS = ['path.to.class.YourBackend']

Aktualisieren Sie Ihren Code, um requestParameter in authenticateMethode für Django 2.1.1
Ganesh

2

Authentifizierung mit E-Mail und Benutzername für 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

Fügen Sie in settings.py die folgende Zeile hinzu:

AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']

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

Und dann in settings.py:

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

Dabei ist Artikel meine Django-App, backends.pydie Python-Datei in meiner App und EmailAuthenticatedie Authentifizierungs-Backend-Klasse in meiner backends.pyDatei


1

Ziemlich einfach. Es sind keine zusätzlichen Klassen erforderlich.

Wenn Sie einen Benutzer mit einer E-Mail erstellen und aktualisieren, legen Sie einfach das Feld Benutzername mit der E-Mail fest.

Wenn Sie das Feld Benutzername authentifizieren, entspricht dies dem Wert der E-Mail.

Der Code:

# 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)

1
Bitte fügen Sie einen Beispielcode hinzu, um zu demonstrieren, wie Ihre Antwort zur Lösung des Problems beitragen kann.
Suit Boy Apps

1
Meine Antwort ist so einfach, dass es offensichtlich sein sollte. Ich werde mich nicht darum kümmern. Das Punktesystem auf Stackoverflow ist mir egal. Ich werde es einfach hier posten: User.objects.create_user (Benutzername = post_data ['email'] etc ...)
Casman Ridder

1
@CasmanRidder Ihre Antwort wird gelöscht, wenn Sie keine zusätzlichen Informationen hinzufügen.
10 Rep

omg Ich muss meinen Beitrag aktualisieren, sonst löschst du meinen Beitrag. Ich muss ihn ändern. Ich muss ich muss ich muss LOL
Casman Ridder

0

Für Django 2

username = get_object_or_404(User, email=data["email"]).username
        user = authenticate(
            request, 
            username = username, 
            password = data["password"]
        )
        login(request, user)

0

Authentifizierung mit E-Mail für 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')

Können Sie ein wenig Text hinzufügen, um zu erklären, was Ihre Antwort bewirkt und wie sie zur Beantwortung der Frage beiträgt?
Jaquez

Bearbeitet. Danke
Shakil Ahmmed

0

Wenn Sie eine benutzerdefinierte Datenbank erstellt haben, können Sie von dort aus Ihre E-Mail-ID und Ihr Kennwort überprüfen.

  1. Holen Sie sich die E-Mail-ID und das Passwort mit models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2. In der abgerufenen Liste zuordnen object_query_list

3. Konvertieren Sie die Liste in einen String

Ex :

  1. Nehmen Sie das HTML Email_idund die PasswordWerte inViews.py

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

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

  2. Rufen Sie die E-Mail-ID und das Kennwort aus der Datenbank ab

    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. Nehmen Sie die Werte für E-Mail-ID und Kennwort in der Liste aus dem eingestellten QueryWert

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. Liste in String konvertieren

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

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

Endlich Ihre Login-Bedingung

if (string_email==u_email and string_password ==u_pass)

0

Ich habe einen Helfer dafür erstellt: Funktion 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)

0

Es scheint, dass die Methode dazu mit Django 3.0 aktualisiert wurde.

Eine Arbeitsmethode für mich war:

authentication.py # <- Ich habe dies in eine App eingefügt (funktionierte nicht im Projektordner neben 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

Fügen Sie dies dann der Datei settings.py hinzu

AUTHENTICATION_BACKENDS = (
    'appname.authentication.EmailBackend',
)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.