Django开发笔记(九)Django组件(上)cookie和session
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
Django的中间件的定义:
Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output. MiddleWare,是 Django 请求/响应处理的钩子框架。 它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。
【输入指代的就是客户端像服务端django发送数据,输出指代django根据客户端要求处理数据的结果返回给客户端】
如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
django框架内部声明了很多的中间件,这些中间件有着各种各种的用途,有些没有被使用,有些被默认开启使用了。而被开启使用的中间件,都是在settngs.py的MIDDLEWARE中注册使用的。
Django默认的Middleware:(在settings.py文件中定义,我们以后自定义的中间件也要在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',
]
中间件所在的位置:老员工流程图又出来了

一、自定义中间件
1.示例代码
1).定义中间件创建存放自定义中间件的文件这里选择在app01里创建mds.py文件,里面定义两个中间件MD1和MD2:
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self,request): # 这个process_request可一个字都不能错哈,就叫这个名字
'''
:param request: 请求信息对象
:return:
'''
print("MD1 process_request")
def process_response(self,request,response):
'''
:param request: 请求信息对象
:param response: 视图函数返回的响应体
:return:
'''
print("MD1 process_response")
return response
class MD2(MiddlewareMixin):
def process_request(self,request):
'''
:param request: 请求信息对象
:return:
'''
print("MD2 process_request")
def process_response(self,request,response):
'''
:param request: 请求信息对象
:param response: 视图函数返回的响应体
:return:
'''
print("MD2 process_response")
return response
- process_request默认返回None,返回None,则继续执行下一个中间件的process_request;一旦返回响应体对象,则会拦截返回。
- process_response必须有一个形参response,并return response;这是view函数返回的响应体,像接力棒一样传承给最后的客户端。
2).在settings文件中注册中间件:

3).构建路由和视图函数:
# path('index/',index)
def index(request):
print("执行index视图函数")
return HttpResponse("<h1> hello </h1>")
4).启动项目,访问,后台打印:

由此我们可以看出中间件的执行顺序:

2.process_request
process_request默认返回None,返回None,则继续执行下一个中间件的process_request;一旦返回响应体对象,则会拦截返回。
我们刚刚写的示例request里什么都没做,现在我们做一个拦截非法IP的功能:
class MD1(MiddlewareMixin):
def process_request(self,request): # 这个process_request可一个字都不能错哈,就叫这个名字
'''
:param request: 请求信息对象
:return:
'''
print("MD1 process_request")
# 非法IP识别
visit_ip = request.META.get("REMOTE_ADDR")
if visit_ip in ['127.0.0.1','127.0.0.2']:
return HttpResponse("非法IP!") # 返回了响应体,拦截,原路返回
return None
这里的拦截,就是不执行下面的中间件了,原路返回,上面的中间件是可以执行的:

3.process_response
对响应进行处理,希望不管返回什么响应,我都能在前头加一个“hi”,这么一个功能:
def process_response(self,request,response):
'''
:param request: 请求信息对象
:param response: 视图函数返回的响应体
:return:
'''
print("MD1 process_response")
response.content = b"hi"+response.content
return response
二、cookie与session
我们知道HTTP协议是无状态协议,也就是说每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
1.cookie
Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!
cookie可以理解为每一个浏览器针对每一个服务器创建的key-value结构的本地存储文件
2.cookie语法与案例
cookie基本语法:
(1) 设置cookie:
res = HttpResponse(...) 或
rep = render(request, ...) 或
rep = redirect()res.set_cookie(key,value,max_age...) # 不咋安全
res.set_signed_cookie(key,value,salt='加密盐',...) # 这种方法更安全
(2) 获取cookie: request.COOKIES
(3) 删除cookie response.delete_cookie("cookie_key",path="/",domain=name)
简单案例:做一个登录的功能(路由代码省略)。
views.py:
from django.shortcuts import render,redirect,HttpResponse
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
def index(request):
# 判断该客户端是否登录
is_login = request.COOKIES.get("is_login")
if(is_login):
return render(request, "index.html")
else:
return render(request,"login.html")
# @csrf_exempt
def login(request):
if request.method == "GET":
return render(request, "login.html")
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
if user == "bj" and pwd == "123":
# 登录成功,写一个小cookie
res = HttpResponse("登录成功")
res.set_cookie("is_login","TRUE")
return res
# return redirect("/app01/index") # 关于重定向的这个路径
else:
return render(request,"login.html")
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>hello index</h3>
</body>
</html>
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
用户名:<input type="text" name = "user">
密码:<input type="password" name="pwd">
<input type="submit">
</form>
</body>
</html>
3.基于Django的session实现机制
cookie致命缺陷是不安全,将所有敏感数据交给客户端也就是浏览器,没有办法保证数据不丢失不泄露。所以我们要自己保存数据,浏览器只存一把钥匙就够了。
Django 提供对匿名会话(session)的完全支持。这个会话框架让你可以存储和取回每个站点访客任意数据。它在服务器端存储数据, 并以cookies的形式进行发送和接受数据。
session流程图:
写session的时候完成了三件事:创建一把钥匙;插入一条记录到表里;响应这把钥匙给客户端。
读session的时候完成了三件事:取钥匙;去表里匹配;匹配到了就取出来匹配到的键值对。

4.session语法使用
- session 在服务器端,cookie 在客户端(浏览器)
- session 默认被存在在服务器的一个文件里(不是内存)
- session 的运行依赖 session id,而 session id 是存在 cookie 中的.
- session 可以放在 文件、数据库、或内存中都可以。
- 用户验证这种场合一般会用 session
# Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
# 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
基于session实现登录案例:
表模型和路由省略。
views.py:
from django.shortcuts import render, redirect, HttpResponse
from app02.models import User
# Create your views here.
def index(request):
# 读session
# 1.取session_id的钥匙
# 2.取Django_session表中找对应的session_data
# 3.session_data.get("user_id")
user_id = request.session.get("user_id") # 不是很懂,这个user_id字段哪来的
if user_id:
# 登录成功过
user = User.objects.get(pk=user_id)
return render(request, "app02/index.html", {"user_name": user.name}) # 不是很懂555
else:
return redirect("/app02/login")
pass
def login(request):
if request.method == "GET":
return render(request, "app02/login.html")
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
try:
user = User.objects.get(name=user, pwd=pwd)
# 写session 就一句话,不必深究里面的实现,先阶段会用就行
# 1.创建一个随机字符串
# 2.将随机字符串作为session_key,将session键值对作为session_data存进数据库表django_session里
# 3.将session_id和随机字符串组成键值对作为cookie返回给客户端
request.session["user_id"] = user.pk
return HttpResponse("登录成功")
except Exception as e:
return redirect("/app02/login") # 这里 必须是/app02,写成app02/login会拼接成 /app02/login/app02/login
5.基于session实现的最后访问时间案例
在上面案例的基础上完善一些功能,代码先省略。待补充。
三、用户认证组件
其实就是基于session又做了一层封装,但是它只针对跟用户相关的比如说登录,注册,校验密码这些。
Django默认已经提供了认证系统Auth模块,我们认证的时候,会使用auth模块里面给我们提供的表。认证系统包含:
- 用户管理
- 权限
- 用户组
- 密码哈希系统
- 用户登录或内容显示的表单和视图
- 一个可插拔的后台系统 admin
1.Django用户模型类
Django认证系统中提供了用户模型类User保存用户的数据,这张表叫auth_user,是Django自带的,不是我们创建的。默认的User包含以下常见的基本字段:
| 字段名 | 字段描述 |
|---|---|
username |
必选。150个字符以内。 用户名可能包含字母数字,_,@,+ . 和-个字符。 |
first_name |
可选(blank=True)。 少于等于30个字符。 |
last_name |
可选(blank=True)。 少于等于30个字符。 |
email |
可选(blank=True)。 邮箱地址。 |
password |
必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。 |
groups |
与Group 之间的多对多关系。 |
user_permissions |
与Permission 之间的多对多关系。 |
is_staff |
布尔值。 设置用户是否可以访问Admin 站点。 |
is_active |
布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。 |
is_superuser |
是否是超级用户。超级用户具有所有权限。 |
last_login |
用户最后一次登录的时间。 |
date_joined |
账户创建的时间。 当账号创建时,默认设置为当前的date/time。 |
上面缺少一些字段,所以后面我们会对当前内置的用户模型进行改造,比如说它里面没有手机号字段,后面我们需要加上。
2.重要方法
Django 用户认证(Auth)组件需要导入 auth 模块
# 认证模块 from django.contrib import auth # 对应数据库用户表,可以继承扩展 from django.contrib.auth.models import User
1)用户对象
create() # 创建一个普通用户,密码是明文的。 create_user() # 创建一个普通用户,密码是密文的。 create_superuser() # 与create_user() 相同,但是设置is_staff 和is_superuser为True。 set_password(*raw_password*) # 设置用户的密码为给定的原始字符串,并负责密码的。 不会保存User对象。当None为raw_password时,密码将设置为一个不可用的密码。 check_password(*raw_password*) # 如果给定的raw_password是用户的真实密码,则返回True,可以在校验用户密码时使用。
2)认证方法
auth.authenticate(username,password) # 将输入的密码转为密文去认证,认证成功返回用户对象,失败则返回None
3)登录和注销方法
from django.contrib import auth # 该函数接受一个HttpRequest对象,以及一个认证了的User对象。此函数使用django的session框架给某个已认证的用户附加上session id等信息。 auth.login() # 该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。 auth.logout()
4)request.user
5)自定义用户表
from django.contrib.auth.models import AbstractUser
设置Auth认证模块使用的用户模型为我们自己定义的用户模型
格式:“子应用目录名.模型类名”
AUTH_USER_MODEL = 'users.User'
6)实例代码
from django.shortcuts import render
# Create your views here.
from django.shortcuts import render, redirect, HttpResponse
# 认证模块
from django.contrib import auth
# 对应数据库用户表,可以继承扩展
from django.contrib.auth.models import User
def index(request):
# request.user:当前登录对象
'''
在AuthenticationMiddleware的process_request方法中做了一件事:
1. user_id = request.session["user_id"]
2.from django.contrib.auth.models import User
在这张user表中找我们刚刚注册了的用户,找到了那么这个user就取到了
if user:
request.user = user
else:
request.user = AnonymousUser # 匿名用户的特点是多有的属性都为零值
结论:在任何视图函数中都可以使用request.user,
如果之前登录成功过即执行过auth.login(),那么request.user=登录对象
如果之前没有登录成功过,request.user=匿名对象
'''
if request.user.id:
# 不为零值即登录成功过
return render(request, 'user_auth/index.html')
else:
return redirect("/user_auth/login")
def login(request):
if request.method == "GET":
return render(request, "user_auth/login.html")
else:
user = request.POST.get("user")
pwd = request.POST.get("pwd")
# user = User.objects.get(username=user,password=pwd) # 不能这么匹配,因为密码Django加密了匹配不上
# authenticate会明文密码转加密再匹配,认证成功返回对象,失败返回None
user = auth.authenticate(username=user, password=pwd)
if user:
# 认证成功
# auth.login做了很多,我们只关心request.session["user_id"] = user.pk这个,不需要我们自己做了
auth.login(request, user)
return redirect("/user_auth/index")
else:
return redirect("/user_auth/login")
def logout(request):
# request.session.flush() 这个是无脑全清
auth.logout(request)
return redirect("/user_auth/login")
浙公网安备 33010602011771号