1.一般django默认使用的验证是用户加密码,如果在用户数据表中含有邮箱字段,并且想要通过邮箱验证用户登录该怎么实现呢。方法如下。
一.自定义验证backend
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
class CustomBackends(ModelBackend):
#自定义验证方法,通过邮箱或者用户名登陆
def authenticate(self, username=None, password=None, **kwargs):
try:
user=UserProfile.objects.get(Q(username=username) | Q(email=username))
if user.check_password(password):
return user
except Exception as e:
return None
自定义的验证backend需要集成django自带的ModelBackend,并且重写authenticate方法,然后再settings配置文件中加入下面这句配置
AUTHENTICATION_BACKENDS=(
'users.models.CustomBackends',
)
其中users是我的一个django中的app,我将CustomBackends这个自定义backend放在了其中的models文件中。
二.view方法和前段代码编写
view函数
from django.shortcuts import render
from django.contrib.auth import authenticate,login
from django.views.generic import View
class LoginView(View):
def get(self,request):
return render(request, "login.html", {})
def post(self,request):
login_form=LoginForm(request.POST)
if login_form.is_valid():
user_name = request.POST.get("username", "")
pass_word = request.POST.get("password", "")
user = authenticate(username=user_name, password=pass_word)
if user is not None:
login(request, user)
return render(request, "index.html")
else:
return render(request, "login.html", {"msg": "用户名,邮箱或密码错误"})
else:
return render(request, "login.html", {"login_form":login_form})
前段表单代码
<form action="/login/" method="post" autocomplete="off">
<input type='hidden' name='csrfmiddlewaretoken' value='mymQDzHWl2REXIfPMg2mJaLqDfaS1sD5' />
<div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}" >
<label>用 户 名</label>
<input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
</div>
<div class="form-group marb8 {% if login_form.errors.password %}errorput{% endif %}">
<label>密 码</label>
<input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
</div>
<div class="error btns login-form-tips" id="jsLoginTips">{% for key,error in login_form.errors.items %}{{error}}{% endfor %}{{msg}}</div>
<div class="auto-box marb38">
<a class="fr" href="forgetpwd.html">忘记密码?</a>
</div>
<input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > " />
<input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy' />
{% csrf_token %}
</form>
那么我们底层是到底如何实现验证的呢,我们可以结合实际代码来讲解,由于本人不懂前端,只能略作分析。首先关注 <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
和<input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
这两句,当我们点击submit登录按钮后前端会向后端发起一个post请求,我们可以通过debug模式来查看该post请求中的具体内容
我们看到在request中有一个POST属性,是一个字典类型数据。
其中有username和password键值对,所以我们可以知道前端向后端发起的POST请求的内容是根据INPUT 标签中的name属性来生成的,所以我们通过request.POST.get(“username”,"")来获取用户名,所以我们如果在前端传入的是用户的邮箱那么我们就能得到如下图。
可以看到我们POST中的键还是username,但是内容已经是邮箱,所以我们可以基于此点来修改验证方式,在看看我们开头出所写到的自定义backend
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
class CustomBackends(ModelBackend):
#自定义验证方法,通过邮箱或者用户名登陆
def authenticate(self, username=None, password=None, **kwargs):
try:
user=UserProfile.objects.get(Q(username=username) | Q(email=username))
if user.check_password(password):
return user
except Exception as e:
return None
我们可以看到在CoustBackends中和LoginView中都有一个authenicate函数,那么这两个authenticate之间有什么关联呢。我们可以先看看LoginView中的authenticate源码
def authenticate(**credentials):
"""
If the given credentials are valid, return a User object.
"""
for backend, backend_path in _get_backends(return_tuples=True):
try:
inspect.getcallargs(backend.authenticate, **credentials)
except TypeError:
# This backend doesn't accept these credentials as arguments. Try the next one.
continue
try:
user = backend.authenticate(**credentials)
except PermissionDenied:
# This backend says to stop in our tracks - this user should not be allowed in at all.
return None
if user is None:
continue
# Annotate the user object with the path of the backend.
user.backend = backend_path
return user
# The credentials supplied are invalid to all backends, fire signal
user_login_failed.send(sender=__name__,
credentials=_clean_credentials(credentials))
首先我们可以看到其实在authenticate方法内部还调用了backend.authenticate方法,那这个backend是什么呢。我们可以看看_get_backends方法到底做了什么
def _get_backends(return_tuples=False):
backends = []
for backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
backends.append((backend, backend_path) if return_tuples else backend)
'No authentication backends have been defined. Does '
'AUTHENTICATION_BACKENDS contain anything?'
)
return backends
看到没,其实_get_backends就是获取了我们在settings总配置的AUTHENTICATION_BACKENDS元组,其实到这所有都一目了然,其实LoginView中的authenticate方法就是调用了我们自定义backend中authenticate方法,所以我们backend中的authenticate方法中username参数就是前端request请求中的POST属性中的username值,所以我们可以通过django中的Q对象来根据username或者email来获取用户。
三.总结
自定义验证方法的步骤
1.自定义Backend类并重构authenticate方法
2.settings中配置AUTHENTICATION_BACKENDS属性
3.编写相应view方法。