目录
一、用户名登录
二、多账号登录
三、首页用户名展示
四、退出登录
五、判断用户是否登录
1. 用户名登录逻辑分析
2. 用户名登录接口设计
1. 请求方式
选项 方案 请求方法 POST 请求地址 /login/
2. 请求参数:表单
参数名 类型 是否必传 说明 username string 是 用户名 password string 是 密码 remembered string 是 是否记住用户
3. 响应结果:HTML
字段 说明 登录失败 响应错误提示 登录成功 重定向到首页
3. 用户名登录后端逻辑(apps.users.views.py)
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 = authenticate(username=username, password=password)
if user is None:
return render(request, 'login.html', {'account_errmsg': '账号或密码错误'})
# 状态保持
login(request, user)
# 使用remembered确定状态保持周期(实现记住登录)
if remembered != 'on':
# 没有记住登录:状态保持在浏览器会话结束后就销毁
request.session.set_expiry(0) # 单位是秒
else:
# 记住登录:状态保持周期为两周:默认是两周
request.session.set_expiry(None)
# 响应结果
# 先取出next
next = request.GET.get('next')
if next:
# 重定向到next
response = redirect(next)
else:
# 重定向到首页
response = redirect(reverse('contents:index'))
# 为了实现在首页的右上角展示用户名信息,我们需要将用户名缓存到cookie中
# response.set_cookie('key', 'val', 'expiry')
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
# 用户登录成功,合并cookie购物车到redis购物车
response = merge_carts_cookies_redis(request=request, user=user, response=response)
# 响应结果
return response
Django自带的用户认证后端默认是使用用户名实现用户认证的。
用户认证后端位置:django.contrib.auth.backends.ModelBackend。
如果想实现 用户名和手机号都可以认证用户,就需要自定义用户认证后端。
自定义用户认证后端步骤
- 在users应用中新建utils.py文件
- 新建类,继承自ModelBackend
- 重写认证authenticate()方法
- 分别使用用户名和手机号查询用户
- 返回查询到的用户实例
users.utils.py
from django.contrib.auth.backends import ModelBackend import re from .models import User def get_user_by_account(account): """ 根据account查询用户 :param account: 用户名或者手机号 :return: user """ try: if re.match('^1[3-9]\d{9}$', account): # 手机号登录 user = User.objects.get(mobile=account) else: # 用户名登录 user = User.objects.get(username=account) except User.DoesNotExist: return None else: return user class UsernameMobileAuthBackend(ModelBackend): """自定义用户认证后端""" def authenticate(self, request, username=None, password=None, **kwargs): """ 重写认证方法,实现多账号登录 :param request: 请求对象 :param username: 用户名 :param password: 密码 :param kwargs: 其他参数 :return: user """ # 根据传入的username获取user对象。username可以是手机号也可以是账号 user = get_user_by_account(username) # 校验user是否存在并校验密码是否正确 if user and user.check_password(password): return user
Django自带认证后端源码:
在配置文件中配置自定义用户认证后端:
# 指定自定义的用户认证后端
AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileAuthBackend']
1. 首页用户名展示方案
方案一
- 模板中 request 变量直接渲染用户名
- 缺点:不方便做首页静态化
{% if user.is_authenticated %}
欢迎您:{{ user.username }} | 退出{% else %} {% endif %}
方案二
- 发送ajax请求获取用户信息
- 缺点:需要发送网络请求
{# ajax渲染 #}
方案三
- Vue读取cookie渲染用户信息
欢迎您:[[ username ]] | 退出
结论:
- 对比此三个方案,我们在本项目中选择 方案三
实现步骤:
- 注册或登录后,用户名写入到cookie
- Vue渲染主页用户名
登录方法 class LoginView(View) 中添加:
# 响应登录结果 response = redirect(reverse('contents:index')) # 登录时用户名写入到cookie,有效期15天 response.set_cookie('username', user.username, max_age=3600 * 24 * 15) return response
3. Vue渲染首页用户名
1.index.html
欢迎您:[[ username ]] | 退出2.index.js
mounted(){ // 获取cookie中的用户名 this.username = getCookie('username'); },
1. logout()方法介绍
退出登录:
- 回顾登录:将通过认证的用户的唯一标识信息,写入到当前session会话中
- 退出登录:正好和登录相反(清理session会话信息)
logout()方法:
- Django用户认证系统提供了
logout()
方法- 封装了清理session的操作,帮助我们快速实现登出一个用户
logout()位置:
django.contrib.auth.__init__.py
文件中logout(request)
2. logout()方法使用
class LogoutView(View): """退出登录""" def get(self, request): """实现退出登录逻辑""" # 清理session logout(request) # 退出登录,重定向到登录页 response = redirect(reverse('contents:index')) # 退出登录时清除cookie中的username response.delete_cookie('username') return response
1. is_authenticate
判断用户是否登录
介绍:
- Django用户认证系统提供了方法
request.user.is_authenticated()
来判断用户是否登录。- 如果通过登录验证则返回True。反之,返回False。
- 缺点:登录验证逻辑很多地方都需要,所以该代码需要重复编码好多次。
class UserInfoView(View): """用户中心""" def get(self, request): """提供个人信息界面""" if request.user.is_authenticated(): return render(request, 'user_center_info.html') else: return redirect(reverse('users:login'))
2. login_required装饰器
判断用户是否登录
定义扩展类方便项目中导入和使用(
utils.views.py
)from django.contrib.auth.mixins import LoginRequiredMixin from django import http from wangye_mall.utils.response_code import RETCODE class LoginRequiredJSONMixin(LoginRequiredMixin): """自定义判断用户是否登录的扩展类:返回JSON""" # 为什么只需要重写handle_no_permission? # 因为判断用户是否登录的操作,父类已经完成,子类只需要关心,如果用户未登录,对应怎样的操作 def handle_no_permission(self): """直接响应JSON数据""" return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录'})
users.views.py
class UserInfoView(LoginRequiredMixin, View): """用户中心""" def get(self, request): """提供用户中心页面""" # 如果LoginRequiredMixin判断出用户已登录,那么request.user就是登陆用户对象 context = { 'username': request.user.username, 'mobile': request.user.mobile, 'email': request.user.email, 'email_active': request.user.email_active } return render(request, 'user_center_info.html', context)
ps:子应用urls.py记得添加
# 用户登录
url(r'^login/$', views.LoginView.as_view(), name='login'),
# 用户退出登录
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
# 用户中心
url(r'^info/$', views.UserInfoView.as_view(), name='info'),