Django - Đăng nhập bằng Email


84

Tôi muốn django xác thực người dùng qua email, không phải qua tên người dùng. Một cách có thể là cung cấp giá trị email dưới dạng giá trị tên người dùng, nhưng tôi không muốn điều đó. Lý do là, tôi có một url /profile/<username>/, do đó tôi không thể có một url /profile/abcd@gmail.com/.

Một lý do khác là tất cả các email là duy nhất, nhưng đôi khi vẫn xảy ra trường hợp tên người dùng đã được sử dụng. Do đó tôi đang tự động tạo tên người dùng là fullName_ID.

Làm cách nào tôi có thể thay đổi để Django xác thực bằng email?

Đây là cách tôi tạo một người dùng.

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

Đây là cách tôi đăng nhập.

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)

Có cách đăng nhập nào khác ngoài việc lấy tên người dùng trước không?

Câu trả lời:


103

Bạn nên viết một chương trình phụ trợ xác thực tùy chỉnh. Một cái gì đó như thế này sẽ hoạt động:

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

Sau đó, đặt chương trình phụ trợ đó làm chương trình phụ trợ xác thực trong cài đặt của bạn:

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

Đã cập nhật . Kế thừa từ ModelBackendkhi nó triển khai các phương thức như get_user()đã có.

Xem tài liệu tại đây: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend


6
Sử dụng django 1.9.8, tôi đã gặp lỗi: đối tượng 'EmailBackend' không có thuộc tính 'get_user'. Đã giải quyết bằng cách thêm phương thức 'get_user' theo stackoverflow.com/a/13954358/2647009
baltasvejas

Vui lòng chỉ định phiên bản Django mà mã này có thể hoạt động. Một số phàn nàn về việc thiếu phương thức get_user.
Tiến sĩ Younes Henni

2
Thay vì chỉ là if user.check_password(password):bạn có thể muốn bao gồm những gì Django làm theo mặc định thông qua ModelBackend: if user.check_password(password) and self.user_can_authenticate(user):để kiểm tra xem người dùng có is_active=True.
jmq

1
Điều này không dễ bị tấn công theo thời gian vì nó không bao gồm giảm thiểu Django trên mã nguồn?
Gabriel Garcia

Bây giờ, nội dung của một yêu cầu phải chứa các trường như tên người dùng và mật khẩu. Có cách nào để thay đổi nó thành email và mật khẩu không?
Karol

55

Nếu bạn đang bắt đầu một dự án mới, django thực sự khuyên bạn nên thiết lập một mô hình người dùng tùy chỉnh. (xem https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project )

và nếu bạn đã làm điều đó, hãy thêm ba dòng vào mô hình người dùng của bạn:

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

Sau đó authenticate(email=email, password=password)hoạt động, trong khi authenticate(username=username, password=password)ngừng hoạt động.


3
Khi chạy createuperuser, bản thân nó sẽ tạo ra lỗi: TypeError: create_superuser () thiếu 1 đối số vị trí bắt buộc: 'tên người dùng'. Bạn cần phải sử dụng quản lý người dùng tùy chỉnh: 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
Toàn bộ hướng dẫn tại đây: fomfus.com/articles/…
user2061057

1
Sau đó, một lần nữa, tài liệu Django khuyên bạn không nên sử dụng mô hình người dùng tùy chỉnh nếu bạn đang tạo một ứng dụng có thể sử dụng lại .
djvg

11

Xác thực email cho Django 3.x

Để sử dụng email / tên người dùng và mật khẩu để xác thực thay vì xác thực tên người dùng và mật khẩu mặc định, chúng ta cần ghi đè hai phương thức của lớp ModelBackend: authenticate () và get_user ():

Phương thức get_user lấy user_id - có thể là tên người dùng, ID cơ sở dữ liệu hoặc bất cứ thứ gì, nhưng phải là duy nhất cho đối tượng người dùng của bạn - và trả về đối tượng người dùng hoặc Không có. Nếu bạn không giữ email làm khóa duy nhất, bạn sẽ phải quan tâm đến nhiều kết quả trả về cho query_set. Trong đoạn mã dưới đây, điều này đã được giải quyết bằng cách trả về người dùng đầu tiên từ danh sách trả về.

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

Theo mặc định, AUTHENTICATION_BACKENDS được đặt thành:

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

Trong tệp settings.py, thêm phần sau ở dưới cùng để ghi đè mặc định:

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

10

Tôi đã có một yêu cầu tương tự trong đó tên người dùng / email sẽ hoạt động cho trường tên người dùng.

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

Đồng thời thêm AUTHENTICATION_BACKENDS = ('path.to.CustomBackend',) trong settings.py


Điều này đã làm việc cho tôi cho đến khi tôi nâng cấp từ 1.11 lên 2.1.5. Bất kỳ ý tưởng tại sao nó không hoạt động với phiên bản này?
zerohedge

@zerohedge thêm yêu cầu vào các tham số của phương thức xác thực. Xem docs.djangoproject.com/en/2.2/topics/auth/customizing/…
Văn

Nó cũng để lại cho bạn mở cửa cho một cuộc tấn công thời gian, đó là giá trị cố gắng để bắt chước chặt chẽ thực hiện Django để ngăn chặn các lỗ hổng như: github.com/django/django/blob/master/django/contrib/auth/...
Gabriel Garcia

Điều này cũng sẽ cho phép người dùng không hoạt động xác thực.
Gabriel Garcia

5

Django 2.x

Như đã đề cập bởi Ganesh ở trên cho django 2.x, phương thức xác thực bây giờ yêu cầu một tham số yêu cầu.

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

thêm phần phụ trợ của bạn vào cài đặt dự án của bạn

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

Mô hình Người dùng tùy chỉnh của bạn sẽ cần tạo email duy nhất cho những người dùng đang hoạt động và đã được xác thực, bạn có thể thực hiện việc này đơn giản với những thứ như sau:

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'

Nhưng để ngăn người khác chặn người khác sử dụng email của họ, thay vào đó, bạn nên thêm xác thực email cũng như quy trình đăng ký và đăng nhập của bạn có tính đến các email có thể không phải là duy nhất (và có thể ngăn người dùng mới sử dụng địa chỉ email hiện có và đã được xác thực).


4

Xác thực tên người dùng và email cho Django 2.X

Lưu ý rằng đây là một câu hỏi phổ biến, đây là một triển khai tùy chỉnh bắt chước mã nguồn Django nhưng xác thực người dùng bằng tên người dùng hoặc email, phân biệt chữ hoa chữ thường, giữ bảo vệ tấn công định thờikhông xác thực người dùng không hoạt động .

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

Luôn nhớ thêm nó vào settings của bạn.py một Phần phụ trợ Xác thực chính xác .


1
Sự hiểu biết của tôi có đúng UserModel().set_password(password)là có để ngăn những kẻ tấn công xác định xem người dùng có tồn tại hay không bằng cách thực hiện gần như cùng một lượng công việc mật mã bất kể (tôi cho rằng đây là cuộc tấn công định thời mà bạn muốn nói đến)?
Grand Phuba

1
@GrandPhuba Bạn hoàn toàn đúng
Gabriel Garcia

2

Bạn nên tùy chỉnh lớp ModelBackend. Mã đơn giản của tôi:

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

Và trong tệp settings.py , hãy thêm:

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

Cập nhật mã của bạn để bao gồm requesttham số trong authenticatephương thức cho django 2.1.1
Ganesh

2

Xác thực bằng email và tên người dùng cho 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

Trong settings.py, thêm dòng sau,

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

Và sau đó trong settings.py:

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

trong đó các bài báo là ứng dụng django của tôi, backends.pylà tệp python bên trong ứng dụng của tôi và EmailAuthenticatelà lớp phụ trợ xác thực bên trong backends.pytệp của tôi


1

Khá đơn giản. Không cần thêm bất kỳ lớp học bổ sung nào.

Khi bạn tạo và cập nhật một người dùng bằng email, chỉ cần đặt trường tên người dùng với email.

Bằng cách đó khi bạn xác thực trường tên người dùng sẽ có cùng giá trị của email.

Mật mã:

# 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
Vui lòng thêm một số mã mẫu để giúp chứng minh cách trả lời của bạn có thể giúp giải quyết vấn đề.
Ứng dụng Suit Boy

1
câu trả lời của tôi rất đơn giản, nó phải được hiển nhiên. Tôi sẽ không bận tâm. Tôi không quan tâm đến hệ thống điểm trên Stackoverflow. Tôi sẽ chỉ đăng nó ở đây: User.objects.create_user (tên người dùng = post_data ['email'] vv ...)
Casman Ridder

1
@CasmanRidder Câu trả lời của bạn sẽ bị xóa nếu bạn không thêm thông tin bổ sung.
10 Rep

omg Tôi phải cập nhật bài đăng của mình nếu không bạn sẽ xóa bài đăng của tôi Tôi phải thay đổi ... Tôi phải Tôi phải LOL
Casman Ridder

0

Đối với Django 2

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

0

Xác thực bằng email cho 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')

Bạn có thể thêm một chút văn bản để giải thích câu trả lời của bạn có tác dụng gì và nó giúp trả lời câu hỏi như thế nào không?
Jaquez

Đã chỉnh sửa. Cảm ơn
Shakil Ahmmed

0

Nếu Bạn đã tạo Cơ sở dữ liệu tùy chỉnh, từ đó nếu bạn muốn xác thực id email và mật khẩu của mình.

  1. Tìm nạp id Email và Mật khẩu bằng models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2. gán trong danh sách được tìm nạp object_query_list

3. chuyển đổi danh sách thành chuỗi

Ví dụ :

  1. Lấy Html Email_idPasswordGiá trị trongViews.py

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

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

  2. Tìm nạp id Email và mật khẩu từ cơ sở dữ liệu

    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. Lấy giá trị id Email và mật khẩu trong danh sách từ bộ Querygiá trị

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. Chuyển đổi danh sách thành chuỗi

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

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

Cuối cùng là Điều kiện đăng nhập của bạn

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

0

Tôi đã tạo một trình trợ giúp cho: chức năng 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

Có vẻ như phương pháp thực hiện điều này đã được cập nhật với Django 3.0.

Một phương pháp làm việc đối với tôi là:

xác thực.py # <- Tôi đã đặt nó trong một ứng dụng (không hoạt động trong thư mục dự án cùng với 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

Sau đó, thêm nó vào tệp settings.py

AUTHENTICATION_BACKENDS = (
    'appname.authentication.EmailBackend',
)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.