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

来源:互联网 发布:田宫四驱车淘宝店 编辑:程序博客网 时间:2024/04/26 11:14

上一节说明的index和login页面, 并不需要专门写views, 因为是使用系统自带的templateview


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

1. 写用户登录view

a. 写粗略的users/views

from django.shortcuts import renderdef 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中设置断点


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


pycharm中views断点标蓝色了.

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

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



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

修改users/views代码

from django.shortcuts import renderfrom 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 ModelBackendfrom django.db.models import Q  # Q可以帮助实现并集from .models import UserProfileclass 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 Viewclass 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 formsclass 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信息是一致的.