使用电子邮件和密码验证用户

时间:2021-05-09 19:18:37

I have a form that only allow users to login via username . I decided to only allow user to login via email instead of username.

我有一个表单,只允许用户通过用户名登录。我决定只允许用户通过电子邮件而不是用户名登录。

First, this is not a duplication of any question relating to logging via email because in my scenario, I validate and authenticate the user in forms.py before he proceed to the final login in views so it give me the chance to raise an error for incorrect login passwords etc.

首先,这不是与通过电子邮件进行日志记录有关的任何问题的重复,因为在我的场景中,我在进入最终登录视图之前在forms.py中验证和验证用户,这样我就有机会提出错误错误的登录密码等

The issue i'm facing is I modified my forms.py to raise an error if the email doesn't exist which works but It wouldn't let the user login via his email.

我面临的问题是我修改了我的forms.py以引发错误,如果电子邮件不存在,但它不会让用户通过他的电子邮件登录。

 def LoginRequest(request):
     form = LoginForm(request.POST or None)    
     if request.POST and form.is_valid():
         user = form.login(request)
         if user:
             login(request, user)
             return HttpResponseRedirect(reverse('Hello'))

     return render(request, 'login.html',{'form': form})

This is my original code which only allow users to login via username

这是我的原始代码,只允许用户通过用户名登录

 class LoginForm(forms.Form):
     username = forms.CharField()
     password = forms.CharField(
        widget=forms.PasswordInput(render_value=False)
        )

     def clean(self):        
         username = self.cleaned_data.get('username')
         password = self.cleaned_data.get('password')
         user = authenticate(username=username, password=password)
         if not user or not user.is_active:
             raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
         return self.cleaned_data

     def login(self, request):
         username = self.cleaned_data.get('username')
         password = self.cleaned_data.get('password')
         user = authenticate(username=username, password=password)
         return user

This is my modified code which only allow users to login via email. I thought a lot about how I would do it but this is the best idea I came up with. Sorry it's a bit confusing. The problem is, it wouldn't let the user login. I don't understand why.

这是我修改过的代码,只允许用户通过电子邮件登录。我想了很多我会怎么做但这是我提出的最好的主意。对不起,这有点令人困惑。问题是,它不会让用户登录。我不明白为什么。

 class LoginForm(forms.Form):
     username = forms.CharField()
     password = forms.CharField(
            widget=forms.PasswordInput(render_value=False)
        )

     def clean(self):       
         user = self.cleaned_data.get('username')
         password = self.cleaned_data.get('password')

         if User.objects.filter(email=user).exists():
             password = self.cleaned_data.get('password')
             user = authenticate(username=user.username, password=password)
             if not user or not user.is_active:
                 raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
             return self.cleaned_data

     def login(self, request):
         username = self.cleaned_data.get('username')
         password = self.cleaned_data.get('password')
         user = authenticate(username=username, password=password)
         return user

Can someone please help me?

有人可以帮帮我吗?

3 个解决方案

#1


5  

As dougis pointed out you are trying to authenticate the user using email, But authenticate function authenticates user based on username and password. So here is the trick to authenticate the user using email (complete working form):

正如dougis所指出的那样,您正在尝试使用电子邮件对用户进行身份验证,但身份验证功能会根据用户名和密码对用户进行身份验证。所以这是使用电子邮件验证用户的技巧(完整的工作表单):

from django.db.models import Q
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User
from django import forms
from django.contrib.auth import authenticate

class LoginForm(forms.Form):
    email = forms.CharField()
    password = forms.CharField(
        widget=forms.PasswordInput(render_value=False)
        )

    def clean(self):
        user = self.authenticate_via_email()
        if not user:
            raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
        else:
            self.user = user
        return self.cleaned_data

    def authenticate_user(self):
        return authenticate(
            username=self.user.username,
            password=self.cleaned_data['password'])

    def authenticate_via_email(self):
        """
            Authenticate user using email.
            Returns user object if authenticated else None
        """
        email = self.cleaned_data['email']
        if email:
            try:
                user = User.objects.get(email__iexact=email)
                if user.check_password(self.cleaned_data['password']):
                    return user
            except ObjectDoesNotExist:
                pass
        return None

views.py

views.py

def LoginRequest(request):
    form = LoginForm(request.POST or None)    
    if request.method == 'POST' and form.is_valid():
        user = form.authenticate_user()
        login(request, user)
        return HttpResponseRedirect(reverse('Hello'))

    return render(request, 'login.html',{'form': form})

#2


5  

You can also write a custom backend:

您还可以编写自定义后端:

# yourapp.backends.py    
from django.contrib.auth.models import User


class EmailOrUsernameModelBackend(object):
    """ Authenticate user by username or email """
    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
            else:
                return None
        except User.DoesNotExist:
            return None

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

Now add your backend to settings.py:

现在将您的后端添加到settings.py:

AUTHENTICATION_BACKENDS = (
    'yourapp.backends.EmailOrUsernameModelBackend',
    'django.contrib.auth.backends.ModelBackend',
)

#3


1  

Looks like you are testing against 2 different fields. You look for the user against email

看起来你正在测试2个不同的领域。您根据电子邮件查找用户

if User.objects.filter(email=user).exists():

but then validate against username

但然后验证用户名

user = authenticate(username=user, password=password)

If you are using the email address the auth line should be

如果您使用的是电子邮件地址,则应该使用auth行

user = authenticate(email=user, password=password)

#1


5  

As dougis pointed out you are trying to authenticate the user using email, But authenticate function authenticates user based on username and password. So here is the trick to authenticate the user using email (complete working form):

正如dougis所指出的那样,您正在尝试使用电子邮件对用户进行身份验证,但身份验证功能会根据用户名和密码对用户进行身份验证。所以这是使用电子邮件验证用户的技巧(完整的工作表单):

from django.db.models import Q
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User
from django import forms
from django.contrib.auth import authenticate

class LoginForm(forms.Form):
    email = forms.CharField()
    password = forms.CharField(
        widget=forms.PasswordInput(render_value=False)
        )

    def clean(self):
        user = self.authenticate_via_email()
        if not user:
            raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
        else:
            self.user = user
        return self.cleaned_data

    def authenticate_user(self):
        return authenticate(
            username=self.user.username,
            password=self.cleaned_data['password'])

    def authenticate_via_email(self):
        """
            Authenticate user using email.
            Returns user object if authenticated else None
        """
        email = self.cleaned_data['email']
        if email:
            try:
                user = User.objects.get(email__iexact=email)
                if user.check_password(self.cleaned_data['password']):
                    return user
            except ObjectDoesNotExist:
                pass
        return None

views.py

views.py

def LoginRequest(request):
    form = LoginForm(request.POST or None)    
    if request.method == 'POST' and form.is_valid():
        user = form.authenticate_user()
        login(request, user)
        return HttpResponseRedirect(reverse('Hello'))

    return render(request, 'login.html',{'form': form})

#2


5  

You can also write a custom backend:

您还可以编写自定义后端:

# yourapp.backends.py    
from django.contrib.auth.models import User


class EmailOrUsernameModelBackend(object):
    """ Authenticate user by username or email """
    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
            else:
                return None
        except User.DoesNotExist:
            return None

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

Now add your backend to settings.py:

现在将您的后端添加到settings.py:

AUTHENTICATION_BACKENDS = (
    'yourapp.backends.EmailOrUsernameModelBackend',
    'django.contrib.auth.backends.ModelBackend',
)

#3


1  

Looks like you are testing against 2 different fields. You look for the user against email

看起来你正在测试2个不同的领域。您根据电子邮件查找用户

if User.objects.filter(email=user).exists():

but then validate against username

但然后验证用户名

user = authenticate(username=user, password=password)

If you are using the email address the auth line should be

如果您使用的是电子邮件地址,则应该使用auth行

user = authenticate(email=user, password=password)