用户名登录
登录的核心思想,认证和状态保持,通过用户的认证,确定该登录用户是美多商场的注册用户。通过状态保持缓存用户的唯一标识信息,用于后续是否登录的判断。
1. 用户名登录逻辑分析
2. 用户名登录接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /login/ |
2.请求参数:表单
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
username | string | 是 | 用户名 |
password | string | 是 | 密码 |
remembered | string | 是 | 是否记住用户 |
3.响应结果:HTML
字段 | 说明 |
---|---|
登录失败 | 响应错误提示 |
登录成功 | 重定向到首页 |
3. 用户名登录接口定义
class LoginView(View):
"""用户名登录"""
def get(self, request):
"""
提供登录界面
:param request: 请求对象
:return: 登录界面
"""
pass
def post(self, request):
"""
实现登录逻辑
:param request: 请求对象
:return: 登录结果
"""
pass
4. 用户名登录后端逻辑
class LoginView(View):
"""用户名登录"""
def get(self, request):
"""
提供登录界面
:param request: 请求对象
:return: 登录界面
"""
return render(request, 'login.html')
def post(self, request):
"""
实现登录逻辑
:param request: 请求对象
:return: 登录结果
"""
# 接受参数
username = request.POST.get('username')
password = request.POST.get('password')
remembered = request.POST.get('remembered')
# 校验参数
# 判断参数是否齐全
if not all([username, password]):
return http.HttpResponseForbidden('缺少必传参数')
# 判断用户名是否是5-20个字符
if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
return http.HttpResponseForbidden('请输入正确的用户名或手机号')
# 判断密码是否是8-20个数字
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)
# 设置状态保持的周期
if remembered != 'on':
# 没有记住用户:浏览器会话结束就过期
request.session.set_expiry(0)
else:
# 记住用户:None表示两周后过期
request.session.set_expiry(None)
# 响应登录结果
return redirect(reverse('contents:index'))
多账号登录
Django自带的用户认证系统只会使用用户名去认证一个用户。所以我们为了实现多账号登录,用户名、手机号或者第三方登陆,就需要自定义认证后端,采用其他的唯一信息去认证一个用户
1. 自定义用户认证后端
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
2. 配置自定义用户认证后端
1.Django自带认证后端源码
# 指定自定义的用户认证后端
AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileAuthBackend']
3. 测试自定义用户认证后端
首页用户名展示
1. 首页用户名展示方案
方案一
{% if user.is_authenticated %}
<div class="login_btn fl">
欢迎您:<em>{{ user.username }}</em>
<span>|</span>
<a href="#">退出</a>
</div>
{% else %}
<div class="login_btn fl">
<a href="login.html">登录</a>
<span>|</span>
<a href="register.html">注册</a>
</div>
{% endif %}
方案二
<div class="login_btn fl">
{# ajax渲染 #}
</div>
方案三
- Vue读取cookie渲染用户信息
<div v-if="username" class="login_btn fl">
欢迎您:<em>[[ username ]]</em>
<span>|</span>
<a href="#">退出</a>
</div>
<div v-else class="login_btn fl">
<a href="login.html">登录</a>
<span>|</span>
<a href="register.html">注册</a>
</div>
结论:
- 对比此三个方案,我们在本项目中选择 方案三
实现步骤:
2. 用户名写入到cookie
# 响应注册结果
response = redirect(reverse('contents:index'))
# 注册时用户名写入到cookie,有效期15天
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
return response
# 响应登录结果
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
<div v-if="username" class="login_btn fl">
欢迎您:<em>[[ username ]]</em>
<span>|</span>
<a href="#">退出</a>
</div>
<div v-else class="login_btn fl">
<a href="login.html">登录</a>
<span>|</span>
<a href="register.html">注册</a>
</div>
2.index.js
mounted(){
// 获取cookie中的用户名
this.username = getCookie('username');
},
退出登录
退出登录的核心思想就是清理登录时缓存的状态保持信息。由于首页中用户名是从cookie中读取的。所以退出登录时,需要将cookie中用户名清除。
1. logout()方法介绍
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. 展示用户中心界面
class UserInfoView(View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
需求:
实现方案:
2. is_authenticate
判断用户是否登录
介绍:
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'))
3. login_required装饰器
判断用户是否登录
Django用户认证系统提供了装饰器
login_required
- 内部封装了
is_authenticate
- 位置:
django.contrib.auth.decorators
- 内部封装了
如果通过登录验证则进入到视图内部,执行视图逻辑。
-
LOGIN_URL
配置项指定的地址。
1.装饰
as_view()
方法返回值提示:
结论:
url(r'^info/$', login_required(views.UserInfoView.as_view()), name='info'),
class UserInfoView(View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
2.定义View子类封装
login_required装饰器
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class Loginrequired(View):
"""验证用户是否登陆"""
@classmethod
def as_view(cls, **initkwargs):
# 自定义as_view()方法中,调用父类的as_view()方法
view = super().as_view()
return login_required(view)
class UserInfoView(Loginrequired):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
3.定义obejct子类封装
login_required装饰器
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class Loginrequired(object):
"""验证用户是否登陆"""
@classmethod
def as_view(cls, **initkwargs):
# 自定义as_view()方法中,调用父类的as_view()方法
view = super().as_view()
return login_required(view)
class UserInfoView(Loginrequired, View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
- 提示:定义扩展类方便项目中导入和使用(
meiduo_mall.utils.views.py
)
class LoginrequiredMixin(object):
"""验证用户是否登录扩展类"""
@classmethod
def as_view(cls, **initkwargs):
# 自定义的as_view()方法中,调用父类的as_view()方法
view = super().as_view()
return login_required(view)
class UserInfoView(LoginrequiredMixin, View):
"""用户中心"""
def get(self, request):
"""提供个人信息界面"""
return render(request, 'user_center_info.html')
4. 登录时next参数的使用
1.next参数的效果
http://127.0.0.1:8000/login/?next=/info/
2.next参数的作用
# 响应登录结果
next = request.GET.get('next')
if next:
response = redirect(next)
else:
response = redirect(reverse('contents:index'))
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。