您的位置:首页 > 编程语言 > Go语言

Django建教育平台(七)--用户登录

2017-08-14 22:15 363 查看
上一节说明的index和login页面, 并不需要专门写views, 因为是使用系统自带的templateview



这一节说明用户登录功能, 因为不是使用系统自带的view, 需要自己写login信息输入后的view.

1. 写用户登录view

a. 写粗略的users/views

from django.shortcuts import render

def user_login(request):
if request.method == "POST":
pass
elif request.method == "GET":
return render(request, "login.html", {})


b. 修改urls文件

from users.views import user_login  # 新增代码

urlpatterns = [
url(r'^xadmin/', xadmin.site.urls),

url('^$', TemplateView.as_view(template_name="index.html"), name="index"),
url('^login/$', user_login, name="login")  # 将原来的login写法修改成这样
]


c. 在views中打断点

注意图中def login改成def user_login



d. Run菜单中按Debug

e. 浏览器访问http://127.0.0.1:8000/login/

f. 观察pycharm中断点情况

标蓝色的那一行即是现在运行所在行, 进入了GET的判断分支,  在下方debug窗口可以看到request是WSGIRequest对象.



点击debug窗口中request变量左侧的三角形, 展开request, 可以看到method和path这两者内容:



按F8将剩余代码运行完毕, 然后查看浏览器效果, 已经正常显示login页面.



g. 修改login.html中form action

在login.html中搜索"帐号登录"

修改标红色线处:



修改后:



h. 浏览器确认效果



点击立即登录按钮, 出现CSRF报错



在login.html中增加csrf_token



在浏览器中查看csrf_token的机制, 就是通过跟服务器确认一个随机生成的密钥, 防止恶意post给服务器.

第一步, 进入开发者工具模式.

第二步, 点击①所指的按钮

第三步, 点击②所指的"立即登录"

第四步, 查看③所指的位置, 就能看到django隐藏input的密钥



i. 验证用户身份, 登录

在users/views中设置断点



然后在浏览器输入之前注册的用户和密码, 按"立即登录
4000
".



pycharm中views断点标蓝色了.

在debug窗口中看到request变量, 展开该变量, 能找到里边的POST QueryDict.

展开POST QueryDict, 能看到里边的username, password键值信息, 跟我们刚在浏览器输入的是一样的.



现在就知道该如何取出POST中的内容了.

修改users/views代码

from django.shortcuts import render
from django.contrib.auth import authenticate, login  # 新增代码

def user_login(request):
if request.method == "POST":
user_name = request.POST.get("username", "")  # 新增, 根据刚才断点的分析结果, 用字典方法取出username的值
pass_word = request.POST.get("password", "")  # 新增,  根据刚才断点的分析结果,  用字典方法取出password的值
user = authenticate(username=user_name, password=pass_word)  # 新增, 利用django自带的authenticate方法来确认这个用户是否合法, 如果合法, 则user是一个非空对象.
if user is not None:  # 如果该用户合法, 则user非空.
login(request, user)  # django自带的login方法
return render(request, "index.html")  # 登录成功后返回首页
elif request.method == "GET":
return render(request, "login.html", {})


2. 登录状态的判断

没有登录跟登录之后, 首页的显示状态不同.

未登录是这个样子的, 提示用户登录或者注册.



登陆后, 应该是这样子的, 点击后还能弹出进入个人中心或者退出的按钮.



要实现这样的显示差异, 需要判断是否登录,并作出相应改变.

a. 在index.html中修改以下代码:

<section class="headerwrap ">
<header>
<div  class=" header">
<div class="top">
<div class="wp">
<div class="fl"><p>服务电话:<b>33333333</b></p></div>
<!--登录后跳转-->
{% if request.user.is_authenticated %}  <!判断用户是否通过认证>
<div class="personal">
<dl class="user fr">
<dd>bobby<img class="down fr" src="/static/images/top_down.png"/></dd>
<dt><img width="20" height="20" src="/static/media/image/2016/12/default_big_14.png"/></dt>
</dl>
<div class="userdetail">
<dl>
<dt><img width="80" height="80" src="/static/media/image/2016/12/default_big_14.png"/></dt>
<dd>
<h2>django</h2>
<p>bobby</p>
</dd>
</dl>
<div class="btn">
<a class="personcenter fl" href="usercenter-info.html">进入个人中心</a>
<a class="fr" href="/logout/">退出</a>
</div>
</div>
</div>
{% else %}  <!若用户未通过认证, 则显示以下按钮>
<a style="color:white" class="fr registerbtn" href="register.html">注册</a>
<a style="color:white" class="fr loginbtn" href="login.html">登录</a>
{% endif %}
</div>
</div>

<div class="middle">


在login页面输入已注册的用户名和密码, 按立即登录, 跳转到index页面, index页面中已按照我们的设想显示.



b.非登录状态的验证

但是现在我们网站一直处于登录状态, 为了检查非登陆状态的页面, 我们先进入http://127.0.0.1:8000/xadmin/页面, 把当前用户注销, 然后再访问主页, 得到以下页面.



3. 设置邮箱登录

目前登录只能用用户名登录, 不能用邮箱登录. 

在users/views中添加以下代码:

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q  # Q可以帮助实现并集

from .models import UserProfile

class CustomBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
try:
user = UserProfile.objects.get(Q(username=username) | Q(email=username))  # 会用username或者email对传入的username进行匹配
if user.check_password(password):
return user
except Exception as e:
return None


在settings中增加以下代码:
在installed_apps前面增加代码



AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',  # 逗号不要省
)


然后尝试用邮箱登录, 成功了.

4. 设置登录错误提示

当输入登录帐号或密码错误时, 应该给出提示

a. 在users/views中修改代码:

def user_login(request):
if request.method == "POST":
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": "用户名或密码错误!"})  # 新增 msg 错误提示信息
elif request.method == "GET":
return render(request, "login.html", {})


b. 将msg信息传给login.html

在login.html中以下位置插入{{ msg }}



故意输入错误的用户名或密码, 按登录后出现了提示信息.



5. 用class重写login的view

a. 将users/views中user_login函数注释掉

# def user_login(request):
#     if request.method == "POST":
#         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": "用户名或密码错误!"})
#     elif request.method == "GET":
#         return render(request, "login.html", {})


b. 编写LoginView类

from django.views.generic.base import View  # import View

class LoginView(View):
def get(self, request):  # 重写View的GET方法
return render(request, "login.html", {})  # 将user_login函数的GET判断分支的代码拉过来.

def post(self, request):  # 重写View的POST方法
user_name = request.POST.get("username", "")  # 将user_login函数的POST判断分支的代码拉过来.
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": "用户名或密码错误!"})


经过测试, LoginView类是可以正常实现之前user_login函数的功能的

c. 配置主页的登录链接

由于index.html中设置login页面的链接方式有两种, 一种是/login/, 一种是login.html, 目前url指匹配第一种.



修改urls的匹配设定

urlpatterns = [
url(r'^xadmin/', xadmin.site.urls),

url('^$|^index.html$', TemplateView.as_view(template_name="index.html"), name="index"),  # 正则表达式表示A,B两模式匹配其中之一的, 写成A|B
url('^login/$|login.html$', LoginView.as_view(), name="login"),  # 正则表达式表示A,B两模式匹配其中之一的, 写成A|B
]


6. 用form来检查登录
当用户登录时输入一些不合法信息时, 希望能提前检出不合法性, 减少服务器访问负担, 并提醒用户正确输入.

a. 在users下新建forms.py文件, 输入代码

__author__ = 'Elvan'
__date__ = '2017/8/13 19:39'

from django import forms

class LoginForm(forms.Form):
username = forms.CharField(required=True)  # 将username设置为必填字段
password = forms.CharField(required=True, min_length=5)  # 将password设置为必填字段, 且最短长度是5


b. users/views中修改代码

from .forms import LoginForm
# 中间代码省略
class LoginView(View):
def get(self, request):
return render(request, "login.html", {})

def post(self, request):
    login_form = LoginForm(request.POST)   # 实例化LoginForm类的对象, 需传入request.POST参数
    if login_form.is_valid():   # 检查login_form是否出错, 没出错的才验证用户名和密码
        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})  # 如果login_form出错, 则返回login_from对象, 在前端进一步处理


c. 提取login_form中error信息

在if login_form is_valid(): 这句前打断点



在login页面故意不输入任何信息即点击登录



到断点位置, 按F6 step over, 查看debugger窗口, 把login_form对象展开, 把_errors这个Dict展开, 看到password和username信息.



d. 将error信息传递到login.html

红色边框提示

修改前:

<div class="form-group marb20 ">
<label>用 户 名</label>
<input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
</div>
<div class="form-group marb8 ">
<label>密     码</label>
<input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
</div>


修改后:

<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">{{ msg }}</div>


修改后:

<div class="error btns login-form-tips" id="jsLoginTips">{% for key, error in login_form.errors.items %}{{ error }}{% endfor %}{{ msg }}</div>


浏览器查看效果, 在不输入用户名和密码的时候, 点击登录.



7. session和cookie自动登录机制

为什么需要cookie? 因为http协议本身是一种无状态的协议, 客户端对服务器的每一次请求都是独立的, 请求之间没有联系. 各个客户端对服务器来说都是一样的, 服务器不认客户端.



为了让服务器区别对待不同的客户端, 认客户端, 服务器给客户端发"会员卡".

下图解读, 

客户端A第一次给服务器发请求1;

服务器收到请求1, 发现客户端A是首次来访, 然后就给A发会员卡, 会员卡ID=1.

客户端A收到会员卡后就储存着, 当第二次给服务器发送请求2时, 带上会员卡.

服务器收到请求2, 发现会员卡时, 就给客户端A发送相对应的"会员服务套餐".



浏览器访问百度, 进入开发者工具模式



能看到Cookies是由Name和Value组成的, 类似字典.

同样, 访问本地网站主页时, 也能看到cookies



将用户名和密码在客户端与服务器之间传来传去是危险的, 因此, 服务器会根据客户端传来的cookies, 生成一个session id, 客户端和服务器传递这个session id就可以了.

cookies和session都是储存在本地的(session也存在服务器端), session就存在django数据库中. 

在用户已登录的状态下,查看django_session表.



打开表格, 是可以看到session信息的.



用户名, 密码等信息经过加密, 以字符串的形式储存在session_data键中, 在过期时间之前访问.

expire_date即过期时间, 是可以在django中设定过多长时间失效.

注意, 第二条session_key跟浏览器中cookies信息是一致的.

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐