专栏系列:Django学习教程
前言
目标:实现用户登录和验证码功能。
知识点:表单验证、错误提示、DjangoAuth统一认证、验证码
效果图
编辑
编辑
Django cookie 与 session
Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。
识别返回用户包括三个步骤:
- 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。
- 浏览器将这些信息存储在本地计算机上,以备将来使用。
- 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。
HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。
但是仍然有以下三种方式来维持 Web 客户端和 Web 服务器之间的 session 会话:
Cookies
一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。
在Web开发中,使用 session 来完成会话跟踪,session 底层依赖 Cookie 技术。
编辑
编辑
代码案例
1.编写模板HTML
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户管理系统</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"> <style> .account { width: 400px; border: 1px solid #dddddd; border-radius: 5px; box-shadow: 5px 5px 20px #aaa; margin-left: auto; margin-right: auto; margin-top: 100px; padding: 20px 40px; } .account h2 { margin-top: 10px; text-align: center; } </style> </head> <body> <div class="account"> <h2>用户登录</h2> <form method="post" novalidate> {% csrf_token %} <div class="form-group"> <label>用户名</label> {{ form.username }} <span style="color: red;">{{ form.username.errors.0 }}</span> </div> <div class="form-group"> <label>密码</label> {{ form.password }} <span style="color: red;">{{ form.password.errors.0 }}</span> </div> <div class="form-group"> <label for="id_code">图片验证码</label> <div class="row"> <div class="col-xs-7"> {{ form.code }} <span style="color: red;">{{ form.code.errors.0 }}</span> </div> <div class="col-xs-5"> <img id="image_code" src="/image/code/" onclick="changeImg(this)" style="width: 125px;" /> </div> </div> </div> <input type="submit" value="登 录" class="btn btn-primary"> </form> </div> </body> <script> function changeImg(ths) { // 硬编码 ths.src = '/image/code/?temp=' + Math.random(); } </script> </html>
2.编写模型
from django.db import models class UserInfo(models.Model): username = models.CharField(verbose_name='用户名', max_length=32) password = models.CharField(verbose_name='密码', max_length=64) age = models.IntegerField(verbose_name='年龄') salary = models.DecimalField(verbose_name='工资', max_digits=10, decimal_places=2) gender_choices =( (1, '男'), (2, '女') ) gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices) create_time = models.DateTimeField(verbose_name='创建时间') # dept_id = models.ForeignKey(to=Department, to_field='id', null=True, blank=True, on_delete=models.SET_NULL) dept = models.ForeignKey(verbose_name="部门", to='Department', to_field="id", on_delete=models.CASCADE)
3.编写视图函数
login函数:校验验证码--》验证用户名密码是否正确--》设置用户过期时间为30分钟--》跳转到首页
编辑
登录逻辑图
image_code函数:调用check_code(),生成验证码,设置session60过期。
logout函数:退出登录,删除服务器存储的session并跳转到登陆页面。
views.py
def login(request): if request.method == 'GET': form = LoginForm() return render(request, 'login.html', {'form': form}) form = LoginForm(data=request.POST) if form.is_valid(): # 验证码的校验 user_input_code = form.cleaned_data.pop('code') code = request.session.get('image_code', "") if code.upper() != user_input_code.upper(): form.add_error("code", "验证码错误") return render(request, 'login.html', {'form': form}) # 查询数据库匹配用户名密码是否正确 user_obj = models.UserInfo.objects.filter(**form.cleaned_data).first() print(user_obj) if not user_obj: form.add_error('password', '用户名或密码错误') return render(request, 'login.html', {'form': form}) request.session['info'] = {'id': user_obj.id, 'username': user_obj.username} # 设置 session 过期时间为30分钟 request.session.set_expiry(60 * 30) return redirect('/user/list/') return render(request, 'login.html', {'form': form}) def image_code(request): """ 生成图片验证码 """ # 调用pillow函数,生成图片 img, code_string = check_code() # 写入到自己的session中(以便于后续获取验证码再进行校验) request.session['image_code'] = code_string # 给Session设置60s超时 request.session.set_expiry(60) stream = BytesIO() img.save(stream, 'png') return HttpResponse(stream.getvalue()) def logout(request): request.session.flush() return redirect('/login/') class LoginForm(BootstrapForm): username = forms.CharField( label="用户名", widget=forms.TextInput, required=True ) password = forms.CharField( label="密码", widget=forms.PasswordInput(render_value=True), required=True, ) code = forms.CharField( label="验证码", widget=forms.TextInput, required=True )
4.编写统一过滤(AuthMiddleware)
1.排除那些不需要登录就能访问的页面,例如:login请求,验证码请求
2.读取当前访问的用户的session信息,如果能读到,说明已登陆过,可以访问系统。
3.没有登陆过,也就是在访问系统在浏览器没有用户cookie或者已经失效,则跳转到登陆页面。
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import redirect class AuthMiddleware(MiddlewareMixin): def process_request(self, request): # 0.排除那些不需要登录就能访问的页面 # request.path_info 获取当前用户请求的URL /login/ if request.path_info in ["/login/", "/image/code/"]: return # 1.读取当前访问的用户的session信息,如果能读到,说明已登陆过,就可以继续向后走。 info_dict = request.session.get("info") # print(info_dict) if info_dict: return # 2.没有登录过,重新回到登录页面 return redirect("/login/")
然后在settings.py配置中间件
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'ums.middleware.auth.AuthMiddleware', # 新增位置 ]
5.配置路由
from django.urls import path from ums.views import auth path('user/<int:nid>/edit_user_password/', auth.edit_user_password), path('login/', auth.login), path('logout/', auth.logout), path('image/code/', auth.image_code),
本篇中我使用的是手动绘制验证码,大家可以尝试基于下一章将手动验证码换成自动验证码或者滑动验证码。
如果本文对你有帮助,记得点赞关注,你的支持是我最大的动力!