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/[email protected]/.

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 = `[email protected]`
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?

PythonEnthusiast
quelle

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

Mipadi
quelle
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.

Nikolaus
quelle
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

anuragb26
quelle
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',)
Himanshu Singh
quelle
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).

Bob
quelle
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 .

Gabriel Garcia
quelle
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']
Hùng Ng Vi
quelle
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']
Chetan
quelle
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

Animesh Timsina
quelle
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)
Casman Ridder
quelle
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)
Juan Sebastian Parada Celis
quelle
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')
Shakil Ahmmed
quelle
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)
sagar rathod
quelle
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)
Wariored
quelle
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',
)
Herr Mulla
quelle