手机号或者账号用于登陆
Django自带的用户认证后端默认是使用用户名实现用户认证的
用户认证后端位置:django.contrib.auth.backends.ModelBackend
class ModelBackend(object):
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
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)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
user = UserModel._default_manager.get_by_natural_key(username)
这一句可以简单理解为:
user = User.objects.get(username=username)
视图views.py
from django.contrib.auth import login, authenticate
from users.models import User
class LoginView(View):
"""用户登陆"""
def get(self, request):
"""提供用户登陆页面"""
return render(request, 'login.html')
def post(self, request):
"""实现用户登陆逻辑"""
# 接收参数
username = request.POST.get('username')
password = request.POST.get('password')
remembered = request.POST.get('remembered')
# 校验参数
if not all([username, password]):
return http.HttpResponseForbidden('缺少必传参数')
# 校验用户名
if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
return http.HttpResponseForbidden('请输入正确的用户名或手机号')
# 检验密码
if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
return http.HttpResponseForbidden('密码最少8位,最长20位')
# 认证用户:使用账号查询用户是否存在,如果用户存在,再检验密码是否正确
# user = User.objects.get(username=username)
# user.check_password()
user = authenticate(username=username, password=password)
if user is None:
return render(request, 'login.html', {'account_errmsg': '账号或密码错误'})
# 状态保持
login(request, user)
if remembered != 'on':
# 没有记住登陆: 状态保持在浏览器会话结束后就销毁
request.session.set_expiry(0) # 单位是秒
else:
# 记住登陆:状态保持周期为两周(默认是两周)
request.session.set_expiry(3600)
# 响应结果: 重定向到首页
return redirect(reverse('contents:index'))
由于Django自带的用户认证后端默认是使用用户名实现用户认证的,如果要使用手机号做认证,需要重新定义authenticate方法。
新建文件utils.py
# 自定义用户认证的后端:实现多账号登陆
from django.contrib.auth.backends import ModelBackend
import re
from users.models import User
def get_user_by_account(account):
"""
通过账号获取用户
:param account: 用户名或者手机号
:return: user
"""
try:
if re.match(r'^1[3-9]\d{9}$', account):
# username == 手机号
user = User.objects.get(mobile=account)
else:
# username == 用户名
user = User.objects.get(username=account)
except User.DoesNotExist:
return None
else:
return user
class UsernameMobileBackend(ModelBackend):
"""自定义用户认证后端"""
def authenticate(self, request, username=None, password=None, **kwargs):
"""
重写用户认证的方法
:param request:
:param username: 用户名或手机号
:param password: 密码明文
:param kwargs: 额外参数
:return: user
"""
# 查询用户
user = get_user_by_account(username)
# 如果可以查询到用户,还需要校验密码是否正确
if user and user.check_password(password):
# 返回user
return user
else:
return None
并且在项目配置文件中指定新的AUTHENTICATION_BACKENDS
# 指定自定义用户认证的后端
AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileBackend']
下面分析流程:
views.py的53行打断点
utils.py的14行打断点
后端接收到用户名和明文密码
放通断点
发现调用的是utils.py中自定义的authenticate方法