Django - 使用电子邮件登录
Django - Login with Email
我希望 Django 通过电子邮件而不是用户名来验证用户。一种方法是提供电子邮件值作为用户名值,但我不希望那样。原因是,我有 url /profile/<username>/
,因此我不能有 url /profile/abcd@gmail.com/
.
另一个原因是所有电子邮件都是唯一的,但有时会发生用户名已被占用的情况。因此,我将自动创建用户名 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)
除了先获取用户名,还有其他登录方式吗?
您应该编写自定义身份验证后端。这样的事情会起作用:
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
我有一个类似的要求,其中 username/email 应该适用于用户名 field.In 如果有人正在寻找身份验证后端方法来执行此操作,请查看以下工作 code.You如果您只需要电子邮件,可以更改查询集。
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
同时在 settings.py
中添加 AUTHENTICATION_BACKENDS = ( 'path.to.CustomBackend', )
您应该自定义 ModelBackend class。
我的简单代码:
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
然后在settings.py文件中,添加:
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',
)
其中 articles 是我的 django-app,backends.py
是我应用程序中的 python 文件,EmailAuthenticate
是我 [=13= 中的身份验证后端 class ] 文件
对于 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
您可以通过两种主要方式实现电子邮件身份验证,请注意以下几点:
- 电子邮件应该不在用户模型上是唯一的,以减少拼写错误和恶意使用。
- 电子邮件只有在 已验证 时才能用于身份验证(因为我们已经发送了验证电子邮件并且他们已单击验证 link)。
- 我们应该只发送电子邮件到已验证的电子邮件地址。
自定义用户模型
自定义用户模型是 recommended 开始新项目时,因为更改项目中期可能很棘手。
我们将添加一个 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
模型(需要二次查询),但它可能比 extend 现有项目中现有的 User
模型更好,并且可能是首选取决于登录流程和验证过程。
我们通过 AUTH_USER_MODEL
设置创建从 EmailVerification
到我们项目使用的任何 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
然后我们修改项目 settings.py
以使用我们的身份验证后端。
# project.settings.py
AUTHENTICATION_BACKENDS = ["app.backends.ExtendedUserModelBackend"]
然后您可以 makemigrations
和 migrate
向现有项目添加功能。
备注
- 如果用户名不区分大小写,请将
Q(username__exact=username)
更改为 Q(username__iexact=username)
。
- 在生产中阻止新用户使用现有的经过验证的电子邮件地址注册。
Django 的电子邮件和用户名身份验证2.X
考虑到这是一个常见问题,这里有一个模仿 Django source code but that authenticates the user with either username or email, case-insensitively, keeping the timing attack protection and not authenticating inactive users 的自定义实现。
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
永远记得添加你的 settings.py 正确的 Authentication Backend。
使用 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
在 settings.py 中,添加以下行,
AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']
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和密码进行认证,而不是默认的用户名和密码认证,我们需要覆盖ModelBackend class的两个方法:authenticate()和get_user() :
get_user 方法采用 user_id – 可以是用户名、数据库 ID 或其他任何内容,但必须对您的用户对象是唯一的 – 和 returns 用户对象或 None。如果您没有将电子邮件作为唯一键保存,则必须处理 query_set 返回的多个结果。在下面的代码中,这已通过从返回列表中返回第一个用户来解决。
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
默认情况下,AUTHENTICATION_BACKENDS 设置为:
['django.contrib.auth.backends.ModelBackend']
在 settings.py 文件中,在底部添加以下内容以覆盖默认值:
AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)
如果您创建了自定义数据库,如果您想从那里验证您的电子邮件 ID 和密码。
- 使用
models.objects.value_list('db_columnname').filter(db_emailname=textbox email)
获取电子邮件 ID 和密码
2.assign 在列表中获取 object_query_list
3.Convert 列表到字符串
例如:
取 Html Email_id
和 Password
中的值 Views.py
u_email = request.POST.get('uemail')
u_pass = request.POST.get('upass')
从数据库中获取电子邮件 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)
从 Query
值集
中获取列表中的电子邮件 ID 和密码值
Email_Value = Email[0]
Password_Value=Password[0]
将列表转换为字符串
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)
.
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)
很简单。不需要任何额外的 类.
当您使用电子邮件创建和更新用户时,只需使用电子邮件设置用户名字段。
这样当您验证时,用户名字段将与电子邮件的值相同。
代码:
# 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 # <-- 我把它放在一个应用程序中(在 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
然后将其添加到 settings.py 文件
AUTHENTICATION_BACKENDS = (
'appname.authentication.EmailBackend',
)
默认用户模型继承/扩展了一个抽象class。框架应该对一定数量的变化或改变是宽松的。
一个更简单的技巧是执行以下操作:
这是在虚拟环境中
- 转到您的 django 安装位置并找到 Lib 文件夹
- 导航到 django/contrib/auth/
- 找到并打开 models.py 文件。找到 AbstractUser class 行 315
LINE 336 在 email 属性上添加 unique 并设置为 true
email = models.EmailField(_('email address'), blank=True,unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
- 完成,进行迁移并迁移
这样做需要您自担风险,
2022 年 5 月更新:
此说明说明如何使用 “电子邮件” 和 “密码” 而非 “用户名”设置身份验证" 和 "password" 并且在该指令中,"username" 被删除,我尽量不更改 尽可能使用默认的Django设置。
首先,运行下面的命令创建“帐户”应用程序:
python manage.py startapp account
然后,将"帐户"应用程序设置为"INSTALLED_APPS"并设置AUTH_USER_MODEL = 'account.CustomUser' in "settings.py" 如下图:
# "settings.py"
INSTALLED_APPS = [
# ...
"account", # Here
]
AUTH_USER_MODEL = 'account.CustomUser' # Here
然后,在“帐户”文件夹下创建“managers.py”并创建“CustomUserManager " class 扩展 "UserManager" class in "managers.py" 如图所示以下。 *只需复制并粘贴下面的代码到"managers.py"和"managers.py" =123=] 是使 命令“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)
然后,创建 "CustomUser" class 扩展 "AbstractUser" class 并删除 "username" 设置为 "None" 并设置 "email" 为 "unique=True" 并设置 "email" 为 "USERNAME_FIELD" 并设置 "CustomUserManager" class to "objects" in "account/models.py" 如图以下。 *只需复制并粘贴下面的代码到"account/models.py":
# "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"
或者,您还可以创建 "CustomUser" class 扩展 "AbstractBaseUser" 和 "PermissionsMixin" classes 如下所示。 *下面带有 "AbstractBaseUser" 和 "PermissionsMixin" classes 的代码等同于上面带有 "AbstractUser" class[= 的代码123=] 如您所见,上面带有 "AbstractUser" class 的代码要少得多:
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
然后,在“帐户”文件夹下创建“forms.py”并创建“CustomUserCreationForm " class 扩展 "UserCreationForm" class 和 "CustomUserChangeForm" class 扩展"UserChangeForm" class 并将 "CustomUser" class 设置为 "model" “Meta”内部 class “CustomUserCreationForm”和“CustomUserChangeForm”classes "account/forms.py"=123=]如下图。 *只需复制并粘贴下面的代码到"forms.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',)
然后,创建 "CustomUserAdmin" class 扩展 "UserAdmin" class 并设置 "CustomUserCreationForm" 和 "CustomUserChangeForm" classes 到 "add_form" 和 "form" 分别设置 "AdminPasswordChangeForm" class 为 "change_password_form" 然后设置 "CustomUser " 和 "CustomUserAdmin" classes 到 "admin.site.register()" in "account/admin.py " 如下所示。 *只需复制并粘贴下面的代码到"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
然后,打开下面的url:
http://localhost:8000/admin/login/
最后,您可以使用"email"和"password"登录,如下图:
这是“添加自定义用户”页面,如下所示:
我希望 Django 通过电子邮件而不是用户名来验证用户。一种方法是提供电子邮件值作为用户名值,但我不希望那样。原因是,我有 url /profile/<username>/
,因此我不能有 url /profile/abcd@gmail.com/
.
另一个原因是所有电子邮件都是唯一的,但有时会发生用户名已被占用的情况。因此,我将自动创建用户名 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)
除了先获取用户名,还有其他登录方式吗?
您应该编写自定义身份验证后端。这样的事情会起作用:
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
我有一个类似的要求,其中 username/email 应该适用于用户名 field.In 如果有人正在寻找身份验证后端方法来执行此操作,请查看以下工作 code.You如果您只需要电子邮件,可以更改查询集。
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
同时在 settings.py
中添加 AUTHENTICATION_BACKENDS = ( 'path.to.CustomBackend', )您应该自定义 ModelBackend class。 我的简单代码:
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
然后在settings.py文件中,添加:
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',
)
其中 articles 是我的 django-app,backends.py
是我应用程序中的 python 文件,EmailAuthenticate
是我 [=13= 中的身份验证后端 class ] 文件
对于 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
您可以通过两种主要方式实现电子邮件身份验证,请注意以下几点:
- 电子邮件应该不在用户模型上是唯一的,以减少拼写错误和恶意使用。
- 电子邮件只有在 已验证 时才能用于身份验证(因为我们已经发送了验证电子邮件并且他们已单击验证 link)。
- 我们应该只发送电子邮件到已验证的电子邮件地址。
自定义用户模型
自定义用户模型是 recommended 开始新项目时,因为更改项目中期可能很棘手。
我们将添加一个 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
模型(需要二次查询),但它可能比 extend 现有项目中现有的 User
模型更好,并且可能是首选取决于登录流程和验证过程。
我们通过 AUTH_USER_MODEL
设置创建从 EmailVerification
到我们项目使用的任何 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
然后我们修改项目 settings.py
以使用我们的身份验证后端。
# project.settings.py
AUTHENTICATION_BACKENDS = ["app.backends.ExtendedUserModelBackend"]
然后您可以 makemigrations
和 migrate
向现有项目添加功能。
备注
- 如果用户名不区分大小写,请将
Q(username__exact=username)
更改为Q(username__iexact=username)
。 - 在生产中阻止新用户使用现有的经过验证的电子邮件地址注册。
Django 的电子邮件和用户名身份验证2.X
考虑到这是一个常见问题,这里有一个模仿 Django source code but that authenticates the user with either username or email, case-insensitively, keeping the timing attack protection and not authenticating inactive users 的自定义实现。
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
永远记得添加你的 settings.py 正确的 Authentication Backend。
使用 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
在 settings.py 中,添加以下行,
AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']
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和密码进行认证,而不是默认的用户名和密码认证,我们需要覆盖ModelBackend class的两个方法:authenticate()和get_user() :
get_user 方法采用 user_id – 可以是用户名、数据库 ID 或其他任何内容,但必须对您的用户对象是唯一的 – 和 returns 用户对象或 None。如果您没有将电子邮件作为唯一键保存,则必须处理 query_set 返回的多个结果。在下面的代码中,这已通过从返回列表中返回第一个用户来解决。
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
默认情况下,AUTHENTICATION_BACKENDS 设置为:
['django.contrib.auth.backends.ModelBackend']
在 settings.py 文件中,在底部添加以下内容以覆盖默认值:
AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)
如果您创建了自定义数据库,如果您想从那里验证您的电子邮件 ID 和密码。
- 使用
models.objects.value_list('db_columnname').filter(db_emailname=textbox email)
获取电子邮件 ID 和密码
2.assign 在列表中获取 object_query_list
3.Convert 列表到字符串
例如:
取 Html
Email_id
和Password
中的值Views.py
u_email = request.POST.get('uemail')
u_pass = request.POST.get('upass')
从数据库中获取电子邮件 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)
从
中获取列表中的电子邮件 ID 和密码值Query
值集Email_Value = Email[0]
Password_Value=Password[0]
将列表转换为字符串
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)
.
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)
很简单。不需要任何额外的 类.
当您使用电子邮件创建和更新用户时,只需使用电子邮件设置用户名字段。
这样当您验证时,用户名字段将与电子邮件的值相同。
代码:
# 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 # <-- 我把它放在一个应用程序中(在 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
然后将其添加到 settings.py 文件
AUTHENTICATION_BACKENDS = (
'appname.authentication.EmailBackend',
)
默认用户模型继承/扩展了一个抽象class。框架应该对一定数量的变化或改变是宽松的。
一个更简单的技巧是执行以下操作: 这是在虚拟环境中
- 转到您的 django 安装位置并找到 Lib 文件夹
- 导航到 django/contrib/auth/
- 找到并打开 models.py 文件。找到 AbstractUser class 行 315
LINE 336 在 email 属性上添加 unique 并设置为 true
email = models.EmailField(_('email address'), blank=True,unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
- 完成,进行迁移并迁移
这样做需要您自担风险,
2022 年 5 月更新:
此说明说明如何使用 “电子邮件” 和 “密码” 而非 “用户名”设置身份验证" 和 "password" 并且在该指令中,"username" 被删除,我尽量不更改 尽可能使用默认的Django设置。
首先,运行下面的命令创建“帐户”应用程序:
python manage.py startapp account
然后,将"帐户"应用程序设置为"INSTALLED_APPS"并设置AUTH_USER_MODEL = 'account.CustomUser' in "settings.py" 如下图:
# "settings.py"
INSTALLED_APPS = [
# ...
"account", # Here
]
AUTH_USER_MODEL = 'account.CustomUser' # Here
然后,在“帐户”文件夹下创建“managers.py”并创建“CustomUserManager " class 扩展 "UserManager" class in "managers.py" 如图所示以下。 *只需复制并粘贴下面的代码到"managers.py"和"managers.py" =123=] 是使 命令“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)
然后,创建 "CustomUser" class 扩展 "AbstractUser" class 并删除 "username" 设置为 "None" 并设置 "email" 为 "unique=True" 并设置 "email" 为 "USERNAME_FIELD" 并设置 "CustomUserManager" class to "objects" in "account/models.py" 如图以下。 *只需复制并粘贴下面的代码到"account/models.py":
# "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"
或者,您还可以创建 "CustomUser" class 扩展 "AbstractBaseUser" 和 "PermissionsMixin" classes 如下所示。 *下面带有 "AbstractBaseUser" 和 "PermissionsMixin" classes 的代码等同于上面带有 "AbstractUser" class[= 的代码123=] 如您所见,上面带有 "AbstractUser" class 的代码要少得多:
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
然后,在“帐户”文件夹下创建“forms.py”并创建“CustomUserCreationForm " class 扩展 "UserCreationForm" class 和 "CustomUserChangeForm" class 扩展"UserChangeForm" class 并将 "CustomUser" class 设置为 "model" “Meta”内部 class “CustomUserCreationForm”和“CustomUserChangeForm”classes "account/forms.py"=123=]如下图。 *只需复制并粘贴下面的代码到"forms.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',)
然后,创建 "CustomUserAdmin" class 扩展 "UserAdmin" class 并设置 "CustomUserCreationForm" 和 "CustomUserChangeForm" classes 到 "add_form" 和 "form" 分别设置 "AdminPasswordChangeForm" class 为 "change_password_form" 然后设置 "CustomUser " 和 "CustomUserAdmin" classes 到 "admin.site.register()" in "account/admin.py " 如下所示。 *只需复制并粘贴下面的代码到"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
然后,打开下面的url:
http://localhost:8000/admin/login/
最后,您可以使用"email"和"password"登录,如下图:
这是“添加自定义用户”页面,如下所示: