一、Cookie
(一)由来
HTTP协议的无状态特性导致每次的请求都是独立的,即客户端和服务器在某次会话中产生的数据不会被保存,因此产生了Cookie,用来保存客户端的用户状态
(二)什么是Cookie
- Cookie具体是指服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些信息,以便服务器通过Cookie提取有用信息从而判断访问者
- Cookie最大支持4096字节,保存在客户端,安全性低
(三)Django中操作Cookie
(1)设置Cookie
-
普通
obj.set_cookie(key,value,...)
- obj为HttpResponse或render或redirect实例化的对象
- 加盐
obj.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
- obj为HttpResponse或render或redirect实例化的对象
- 参数
- key:键
- value:值
- max_age:超时时间,以秒为单位,不能给IE设置cookie
- expires:超时时间,以秒为单位,可以给IE设置cookie
- path:cookie生效的路径,/表示根路径,可以被任何URL页面访问
- domain:cookie生效的域名
- secure:布尔值,是否为HTTPS传输
- httponly:布尔值,只能http协议传输,无法被JavaScript获取
# views.py
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'wick' and password == '123':
next_url = request.GET.get('next')
obj = redirect('/home/')
if next_url:
obj = redirect(next_url)
obj.set_cookie('whoami', 'wick',max_age=30)
return obj
return render(request, 'login.html')
(2)获取Cookie
-
获取cookie
request.COOKIES.get('key')
-
获取加盐的cookie值
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
- default: 默认值
- salt: 加密盐
- max_age: 后台控制过期时间
from functools import wraps
def login_auth(func):
@wraps(func)
def inner(request,*args, **kwargs):
print(request.path_info)
print(request.get_full_path())
# 判断当前用户是否登录
if request.COOKIES.get('whoami'):
res = func(request,*args, **kwargs)
return res
else:
target_url = request.path_info
return redirect(f'/login/?next={target_url}')
return inner
(3)删除Cookie
obj.delete_cookie('key'):删除用户浏览器上之前设置的cookie值
@login_auth
def logout(request):
obj = redirect('/login/')
obj.delete_cookie('whoami')
return obj
二、Session
(一)由来
- 解决Cookie的安全性和存储限制问题
- 服务器生成并保存在服务器,可以存储超过4096字节的键值对(依赖于Cookie)
(二)Django中操作Session
- Django中Session存储在django_session表中,第一次使用时需执行迁移命令
- Django中Session失效时间默认是14天
- 同一个浏览器生成的session都会保存在django_session表中同一条数据中
(1)设置Session
-
设置Session时发生的事情:
- Django内部调用算法生成随机字符串
- 将随机字符串和加密后的数据(以及失效时间)存储到django_session表中
- 将随机字符串返回给浏览器,浏览器中以sessionid为键名保存为字典形式
-
设置Session值
request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置
-
设置Session和Cooike的超时时间
request.session.set_expiry(value)
- 如果value是个整数,session会在些秒数后失效。
- 如果value是个datatime或timedelta,session就会在这个时间后失效。
- 如果value是0,用户关闭浏览器session就会失效。
- 如果value是None,session会依赖全局session失效策略
(2)获取Session
-
获取Session时发生的事情:
- Django内部会去请求头中获取cookie中的sessionid对应的随机字符串
- 将该字符串与django_session表中存储的随机字符串进行比对
- 比对成功,会把对应的加密后的数据获取出来,封装到request.session中,否则为空字典
-
获取Session
# 1. Session中数据 request.session['k1'] request.session.get('k1',None) # 2. 会话session的key request.session.session_key # 3. 检查会话session的key在数据库中是否存在 request.session.exists("session_key") # 4. 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems()
(3)删除Session
- del request.session['k1']:删除Session中数据
- request.session.delete():删除当前会话的所有Session数据,数据库中也会删除
- request.session.flush() :删除当前的会话数据并删除会话的Cookie,数据库中也会删除
- request.session.clear_expired():将所有Session失效日期小于当前日期的数据删除,(Django中有自动动删除机制)
三、Django中间件
(一)什么是中间件
- 中间件是一个用来处理Django的请求和响应的框架级别的钩子,本质上是一个自定义类
- 可以全局范围内在视图函数执行前和施行后做一些额外的操作,比如用户登录校验、用户访问频率校验、用户权限校验
- Django中默认有七个中间件
# 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',
]
(二)自定义中间件
- 中间件可以定义五个方法
- 方法中参数request:必需参数,和视图函数中request一样
- 方法中返回值为None时, 继续按照django定义的规则向后继续执行
- 方法中返回值为HttpResponse对象时, 则直接将该对象返回给用户
# 在settings.py的MIDDLEWARE配置项中注册上述两个自定义中间件
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',
'app01.mymiddleware.mymidd.MyMidd1', # 自定义中间件MD1
'app01.mymiddleware.mymidd.MyMidd2' # 自定义中间件MD2
]
(1)process_request
- 默认按照自上而下的顺序执行MIDDLEWARE中每一个中间件内部的process_request方法
- 返回HttpResponse对象:请求会立刻停止从当前中间件的process_response 方法原路返回
# app01/mymiddleware/mymidd.py
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
(2) process_response
- 在视图函数之后 ,默认按照自下而上的顺序执行每个中间件中的process_response 方法
- 必须给方法传入两个形参,request和response
- 必须返回response:response是视图函数返回的HttpResponse对象 ,浏览器才能接受HttpResponse对象
# app01/mymiddleware/mymidd.py
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
(3) process_view
- 执行视图函数之前触发(路由匹配之后)
- 返回 HttpResponse对象 : 直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
- 该方法有四个参数
- request: HttpRequest对象
- view_func:Django即将使用的视图函数对象
- view_args:将传递给视图的位置参数的列表.
- view_kwargs是将传递给视图的关键字参数的字典 (不包括request)
# app01/mymiddleware/mymidd.py
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request(self, request):
print("MD1里面的 process_request")
def process_response(self, request, response):
print("MD1里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD1 中的process_view")
print(view_func, view_func.__name__)
class MD2(MiddlewareMixin):
def process_request(self, request):
print("MD2里面的 process_request")
pass
def process_response(self, request, response):
print("MD2里面的 process_response")
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-" * 80)
print("MD2 中的process_view")
print(view_func, view_func.__name__)
(4) process_exception
- 视图函数出现错误异常自动触发,按照从下往上的顺序执行
- 两个参数 request 和 exception ( 视图函数异常产生的Exception对象 )
- 返回 HttpResponse对象 : 调用模板和中间件中的process_response方法,并返回给浏览器 (不会再执行process_exception 方法)
(5) process_template_response
- 视图函数执行完成后, 视图函数返回的对象有一个render()方法时才会执行,倒序执行
- 两个参数 request 和 response ,即必须返回response
(6)中间件执行流程
- 请求到达中间件, 先按照正序执行每个中间件的process_request方法 ,执行完后匹配路由
- 再顺序执行中间件中的process_view方法 ,执行完后执行视图函数
- 如果视图函数中有异常,会倒序执行process_exception
- 如果视图函数中返回对象有一个render()方法,会倒序执行process_exception方法
- 否则,会倒序执行process_response 方法,将响应发给客户端
- 方法中有response参数的必须返回response(process_template_response、process_response )
- 方法中没有response参数的(process_view、process_exception、process_response )
- 返回none:正常执行
- 返回HttpResponse的:会从当前中间件的response方法依次倒序执行,最终发送给客户端
以上,process_request、process_view默认为顺序,process_exception、process_exception、process_response 默认为倒序
四、Token(扩展)
(一)由来
客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比 ,对数据库和服务器压力大,因此产生了Token
(二)什么是Token
- Token是服务端生成的一串字符串
- 第一次登录时, 服务器生成一个Token返回给客户端
- 再次请求时, 客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码
(三)目的
减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮
(四)使用方式
(1) 设备号/设备mac地址作为Token
- 登录时将 设备号/mac地址 作为参数传给服务端
- 服务端接受后,用变量Token接收并保存在数据库,并将该Token设置到session中 ,下次请求只需比对变量Token
- 优点: 只要登录一次以后一直可以使用
缺点: 客户端需要带设备号/mac地址作为参数传递,而且服务器端还需要保存
若服务器的Token超时后,将客户端传递的Token向数据库中查询,同时并赋值给变量Token,如此,Token的超时又重新计时
(2) session值作为Token
- 携带用户名和密码登陆
- 服务端接受后判断,如果正确, 将本地获取sessionID 作为Token返回给客户端
- 好处:不用存储数据
- 缺点:session过期后要重新登录
(五)一些问题和解决方案
-
问题
在网络不好或者并发请求时会导致多次重复提交数据
-
解决方案( 将session和Token套用 )
- 可以对session进行加锁
- 把token放到session中,当后一个请求到来时,使用session中的token进行比对,当不一致时,即为重复提交,不允许通过