Django-用电子邮件登录

我希望 django 通过电子邮件认证用户,而不是通过用户名。一种方法是提供电子邮件值作为用户名值,但我不想这样。原因是,我有一个网址 /profile/<username>/,因此我不能有一个网址 /profile/abcd@gmail.com/

Another reason being that all emails are unique, but it happen sometimes that the username is already being taken. Hence I'm auto-creating the username as fullName_ID.

我怎样才能改变让 Django 验证电子邮件?

这就是我创建用户的方式。

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

这是我登录的方式。

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)

除了获取用户名之外,还有其他的登录方式吗?

109481 次浏览

您应该编写一个自定义身份验证后端:

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

然后,在设置中将该后端设置为您的授权后端:

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

更新 . 继承自 ModelBackend,因为它已经实现了类似于 get_user()的方法。

点击这里查看文档: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend

I had a similar requirement where either username/email should work for the username field.In case someone is looking for the authentication backend way of doing this,check out the following working code.You can change the queryset if you desire only the email.

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

还要在 setings.py 中添加 AUTHENTATION _ BACKENDS = (‘ path.to. CustomBackend’,)

您应该自定义 ModelBackend 类。 我的简单代码:

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

And in < em > setings.py file, add:

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

如果您正在开始一个新项目,django 强烈建议您设置一个自定义用户模型(参见 https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project)

如果你这样做了,添加三行到你的用户模型:

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

然后 authenticate(email=email, password=password)工作,而 authenticate(username=username, password=password)停止工作。

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

然后在 settings.py:

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

其中文章是我的 django-app,backends.py是我的应用程序中的 python 文件,而 EmailAuthenticate是我的 backends.py文件中的身份验证后端类

For Django 2

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

Django 4.0

有两种主要方法可以实现电子邮件身份验证,请注意以下事项:

  • 电子邮件应该 没有是唯一的用户模型,以减少拼写错误和恶意使用。
  • 电子邮件应该只用于认证,如果他们是 已确认(因为我们已经发送了一个验证电子邮件,他们已经点击了验证链接)。
  • 我们应该发送电子邮件到 已确认的电子邮件地址。

自定义用户模型

一个自定义用户模型是 建议当开始一个新的项目,因为改变中期项目可能是棘手的。

我们将添加一个 email_verified字段,以限制电子邮件身份验证的用户与一个经过验证的电子邮件地址。

# app.models.py
from django.db import models
from django.contrib.auth.models import AbstractUser




class User(AbstractUser):
email_verified = models.BooleanField(default=False)

然后,我们将创建一个自定义身份验证后端,用给定的电子邮件地址替换用户名。

这个后端将处理显式设置 email字段的身份验证表单和设置 username字段的身份验证表单。

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


UserModel = get_user_model()




class CustomUserModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD, kwargs.get(UserModel.EMAIL_FIELD))
if username is None or password is None:
return
try:
user = UserModel._default_manager.get(
Q(username__exact=username) | (Q(email__iexact=username) & Q(email_verified=True))
)
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


然后,我们修改我们的项目 settings.py以使用我们的自定义用户模型和身份验证后端。

# project.settings.py


AUTH_USER_MODEL = "app.User"
AUTHENTICATION_BACKENDS = ["app.backends.CustomUserModelBackend"]


确保在 migrate之前运行 manage.py makemigrations,并且第一次迁移包含这些设置。

扩展用户模型

虽然性能不如自定义 User模型(需要进行二次查询) ,但在现有项目中使用 延伸现有 User模型可能更好,并且根据登录流程和验证过程可能更好。

我们从 EmailVerification创建一个一对一的关系到我们的项目通过 AUTH_USER_MODEL设置使用的 User模型。

# app.models.py
from django.conf import settings
from django.db import models




class EmailVerification(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_query_name="verification"
)
verified = models.BooleanField(default=False)


我们还可以创建一个自定义管理员,其中包括我们的内联扩展。

# app.admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin


from .models import EmailVerification


UserModel = get_user_model()




class VerificationInline(admin.StackedInline):
model = EmailVerification
can_delete = False
verbose_name_plural = 'verification'




class UserAdmin(BaseUserAdmin):
inlines = (VerificationInline,)




admin.site.unregister(UserModel)
admin.site.register(UserModel, UserAdmin)


然后,我们创建一个类似于上面的后端,它只检查相关的模型 verified字段。

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


UserModel = get_user_model()




class ExtendedUserModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD, kwargs.get(UserModel.EMAIL_FIELD))
if username is None or password is None:
return
try:
user = UserModel._default_manager.get(
Q(username__exact=username) | (Q(email__iexact=username) & Q(verification__verified=True))
)
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


We then modify our projects settings.py to use our authentication backend.

# project.settings.py


AUTHENTICATION_BACKENDS = ["app.backends.ExtendedUserModelBackend"]

然后可以使用 makemigrationsmigrate向现有项目添加功能。

笔记

  • 如果用户名不区分大小写,则将 Q(username__exact=username)更改为 Q(username__iexact=username)
  • 在生产中,防止新用户注册现有的经过验证的电子邮件地址。

Django 2.X 的电子邮件和用户名认证

Having in mind that this is a common question, here's a custom implementation mimicking the 姜戈的源代码 but that authenticates the user with either username or email, case-insensitively, keeping the 时间攻击 保护 and 不对非活动用户进行身份验证.

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

一定要记得添加你的设置,使用正确的 认证后端

使用电子邮件和 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

在 setings.py 中,添加以下代码行,

AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']

Authentication with Email For 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')

Django 3.x 的电子邮件身份验证

为了使用 email/username 和 password 进行身份验证,而不是默认的 username 和 password 身份验证,我们需要覆盖 ModelBackend 类的两个方法: enticate ()和 get _ user () :

The get_user method takes a user_id – which could be a username, database ID or whatever, 但是必须对您的用户对象是唯一的 – and returns a user object or None. If you have not kept email as a unique key, you will have to take care of multiple result returned for the query_set. In the below code, this has been taken care of by returning the first user from the returned list.

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

默认情况下,AUTHENTATION _ BACKENDS 设置为:

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

In settings.py file, add following at the bottom to override the default:

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

如果您创建自定义数据库,从那里如果您想验证您的电子邮件 ID 和密码。

  1. Fetch the Email id and Password with models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2.assign in list fetched object_query_list

3. 将 List 转换为 String

Ex :

  1. Take the Html Email_id and Password Values in Views.py

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

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

  2. 从数据库中获取电子邮件 ID 和密码

    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. Query值集中获取列表中的 Email id 和密码值

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. 将 list 转换为 String

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

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

最后您的登录条件

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

我已经为那个函数 authenticate_user(email, password)创建了一个 helper。

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)

非常简单,不需要任何额外的类。

When you create and update a user with an email, just set the username field with the email.

这样,当您验证用户名字段时,该电子邮件的值将是相同的。

密码:

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

这样做的方法似乎已经用 Django 3.0进行了更新。

我的一个工作方法是:

Authentication.py # < ——我把这个放在一个应用程序中(在与 setings.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

Then added this to the settings.py file

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

默认用户模型继承/扩展抽象类。框架应该对一定数量的变更或修改宽容。

一个更简单的方法是这样做: 这是在一个虚拟环境中

  1. 转到 django 安装位置并找到 莉比文件夹
  2. 导航到 django/Contrib/auth/
  3. 查找并打开 models.py 文件,查找 AbstractUser 类 315号线

Email 属性上的 LINE 336添加惟一性并将其设置为 true

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


USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
  1. 完成,移民和移民

这件事你自己承担风险,

2022年5月更新:

这条指令展示了如何使用 “电子邮件”“密码”而不是 用户名“密码”来设置身份验证,在这条指令中,用户名被删除了,我试图不更改 默认的 Django 设置越多越好

首先,运行下面的命令来创建 「帐户」申请:

python manage.py startapp account

然后,将 「帐户」申请设置为 “已安装的应用程序”,并将 AUTH_USER_MODEL = 'account.CustomUser'设置为 “设置.py”,如下所示:

# "settings.py"


INSTALLED_APPS = [
# ...
"account", # Here
]


AUTH_USER_MODEL = 'account.CustomUser' # Here

然后,在 “帐户”文件夹下面创建 “经理们 py”,并在 “经理们 py”中创建扩展 “ UserManager”类“ CustomUserManager”类,如下所示。* 只需要 复制粘贴“经理们 py”“经理们 py”的下面代码就可以使 the command "python manage.py createsuperuser"正常工作而不出错:

# "account/managers.py"


from django.contrib.auth.models import UserManager
from django.contrib.auth.hashers import make_password


class CustomUserManager(UserManager): # Here
   

def _create_user(self, email, password, **extra_fields):
        

if not email:
raise ValueError("The given email must be set")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user


def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)


def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)


if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")


return self._create_user(email, password, **extra_fields)

然后,通过设置 “没有”来创建扩展 “ AbstractUser”类“ CustomUser”类并去除 用户名,将 “电子邮件”设置为 “独特 = 真实”,将 “电子邮件”设置为 “用户名 _ 字段”,并将 “ CustomUserManager”类设置为 “物体”,如下所示。* 只要 “ AbstractUser”类1下面的代码到 “ AbstractUser”类0:

# "account/models.py"


from django.db import models
from django.contrib.auth.models import AbstractUser
from .managers import CustomUserManager


class CustomUser(AbstractUser):
username = None # Here
email = models.EmailField('email address', unique=True) # Here
    

USERNAME_FIELD = 'email' # Here
REQUIRED_FIELDS = []


objects = CustomUserManager() # Here


class Meta:
verbose_name = "custom user"
verbose_name_plural = "custom users"

或者,您也可以创建扩展 “ AbstractBaseUser”和“ PermisonsMixin”类“ CustomUser”类,如下所示。* 以下使用 “ AbstractBaseUser”和“ PermisonsMixin”类的代码相当于以上使用 “ AbstractUser”类的代码,如你所见,以上使用 “ AbstractUser”类的代码要少得多:

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils import timezone
from .managers import CustomUserManager


class CustomUser(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField("first name", max_length=150, blank=True)
last_name = models.CharField("last name", max_length=150, blank=True)
email = models.EmailField('email address', unique=True)
is_staff = models.BooleanField(
"staff status",
default=False,
help_text="Designates whether the user can log into this admin site.",
)
is_active = models.BooleanField(
"active",
default=True,
help_text=
"Designates whether this user should be treated as active. "
"Unselect this instead of deleting accounts."
,
)
date_joined = models.DateTimeField("date joined", default=timezone.now)


USERNAME_FIELD = 'email'


objects = CustomUserManager() # Here

然后,在 “帐户”文件夹下面创建 “表格.py”,并创建延伸 “ UserCreationForm”类"CustomUserCreationForm" class和延伸 “ UserChangeForm”类“ CustomUserChangeForm”类,并在 “帐户”文件夹0的 “ CustomUserCreationForm”和“ CustomUserChangeForm”类“超能力者”的内部阶层中将 “ CustomUser”类设置为 "model",如下所示。* 只要 “帐户”文件夹1下面的代码到 “表格.py”:

# "account/forms.py"


from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser


class CustomUserCreationForm(UserCreationForm):


class Meta:
model = CustomUser
fields = ('email',)


class CustomUserChangeForm(UserChangeForm):


class Meta:
model = CustomUser
fields = ('email',)

然后,创建扩展 "UserAdmin" class“ CustomUserAdmin”类,并将 “ CustomUserCreationForm”和“ CustomUserChangeForm”类分别设置为 "add_form"“表格”,将 “ AdminPasswordChangeForm”类设置为 “ change _ password _ form”,然后将 “ CustomUser”和“ CustomUserAdmin”类设置为 “ account/admin.py”中的 “ admin.site.register ()”,如下所示。* 只要 "UserAdmin" class0下面的代码到 “ account/admin.py”:

from django.contrib import admin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from django.contrib.auth.forms import AdminPasswordChangeForm
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser


class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("password",)}),
("Personal info", {"fields": ("first_name", "last_name", "email")}),
(
"Permissions",
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
("Important dates", {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
add_form = CustomUserCreationForm # Here
form = CustomUserChangeForm # Here
change_password_form = AdminPasswordChangeForm # Here
list_display = ("email", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups")
search_fields = ("first_name", "last_name", "email")
ordering = ("email",)
filter_horizontal = (
"groups",
"user_permissions",
)


admin.site.register(CustomUser, CustomUserAdmin) # Here

然后,运行下面的命令进行迁移和迁移:

python manage.py makemigrations && python manage.py migrate

然后,运行下面的命令创建一个超级用户:

python manage.py createsuperuser

然后,运行下面的命令来运行服务器:

python manage.py runserver 0.0.0.0:8000

然后,打开下面的网址:

http://localhost:8000/admin/login/

最后,您可以使用 “电子邮件”"password"登录,如下所示:

enter image description here

这是 “添加自定义用户”页面,如下图所示:

enter image description here