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

上一节说明的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中设置断点


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


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信息是一致的.





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值