登陆的核心思想,认证和状态保持,经过用户的认证,肯定该登陆用户是美多商场的注册用户。经过状态保持缓存用户的惟一标识信息,用于后续是否登陆的判断。html
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /login/ |
2.请求参数:表单
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
username | string | 是 | 用户名 |
password | string | 是 | 密码 |
remembered | string | 是 | 是否记住用户 |
3.响应结果:HTML
字段 | 说明 |
---|---|
登陆失败 | 响应错误提示 |
登陆成功 | 重定向到首页 |
class LoginView(View): """用户名登陆""" def get(self, request): """ 提供登陆界面 :param request: 请求对象 :return: 登陆界面 """ pass def post(self, request): """ 实现登陆逻辑 :param request: 请求对象 :return: 登陆结果 """ pass
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自带的用户认证系统只会使用用户名去认证一个用户。因此咱们为了实现多帐号登陆,用户名、手机号或者第三方登录,就须要自定义认证后端,采用其余的惟一信息去认证一个用户python
自定义用户认证后端步骤ajax
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
1.Django自带认证后端源码
2.配置自定义用户认证后端
# 指定自定义的用户认证后端 AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileAuthBackend']
方案一django
- 模板中 request 变量直接渲染用户名
- 缺点:不方便作首页静态化
{% 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 %}
方案二后端
- 发送ajax请求获取用户信息
- 缺点:须要发送网络请求
<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>
结论:缓存
- 对比此三个方案,咱们在本项目中选择 方案三
实现步骤:cookie
- 注册或登陆后,用户名写入到cookie
- Vue渲染主页用户名
# 响应注册结果 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
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中用户名清除。网络
退出登陆:session
logout()方法:
logout()
方法logout()位置:
django.contrib.auth.__init__.py
文件中logout(request)
class LogoutView(View): """退出登陆""" def get(self, request): """实现退出登陆逻辑""" # 清理session logout(request) # 退出登陆,重定向到登陆页 response = redirect(reverse('contents:index')) # 退出登陆时清除cookie中的username response.delete_cookie('username') return response
class UserInfoView(View): """用户中心""" def get(self, request): """提供我的信息界面""" return render(request, 'user_center_info.html')
需求:
- 当用户登陆后,才能访问用户中心。
- 若是用户未登陆,就不容许访问用户中心,将用户引导到登陆界面。
实现方案:
- 须要判断用户是否登陆。
- 根据是否登陆的结果,决定用户是否能够访问用户中心。
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'))
login_required装饰器
判断用户是否登陆Django用户认证系统提供了装饰器
login_required
来判断用户是否登陆。
is_authenticate
django.contrib.auth.decorators
若是未经过登陆验证则被重定向到
LOGIN_URL
配置项指定的地址。
以下配置:表示当用户未经过登陆验证时,将用户重定向到登陆页面。
LOGIN_URL = '/login/'
1.装饰
as_view()
方法返回值提示:
login_required装饰器
能够直接装饰函数视图,可是本项目使用的是类视图。as_view()
方法的返回值就是将类视图转成的函数视图。结论:
- 要想使用
login_required装饰器
装饰类视图,能够间接的装饰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装饰器
- 提示:
LoginRequired(object)
依赖于视图类View
,复用性不好。
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装饰器
- 提示:
LoginRequired(object)
不依赖于任何视图类,复用性更强。
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')
4.定义验证用户是否登陆扩展类
- 提示:定义扩展类方便项目中导入和使用(
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')
1.next参数的效果
http://127.0.0.1:8000/login/?next=/info/
2.next参数的做用
- 由Django用户认证系统提供,搭配
login_required装饰器
使用。- 记录了用户未登陆时访问的地址信息,能够帮助咱们实如今用户登陆成功后直接进入未登陆时访问的地址。
# 响应登陆结果 next = request.GET.get('next') if next: response = redirect(next) else: response = redirect(reverse('contents:index'))