django之cookie与session和中间件
一、cookie和session的简介
''' HTTP协议四大特性 1.基于请求响应 2.基于TCP、IP作用于应用层之上协议 3.无状态 4.无连接''' # 我们来看一下无状态 ''' 服务端是无法识别客户端的状态的 在互联网初期所有的客户端访问服务端都是一样的数据 所以就算服务端不记住客户端的转态也没事 但是随着互联网的发展有些服务端就需要记住客户端的状态 eg:淘宝、京东... 如果淘宝记不住客户端的状态的话 那么有可能造成我刚刚购买了一个商品然后退出服务端 下次在登入服务端就没记住过你购买过商品有需要查询购买 所以服务端不得不想办法记住客户端的状态 cookie与session就应运而生了''' # cookie ''' 我们每次访问服务端都需要发送信息来证明自己就比如用户名和密码 然后当我们第一次访问服务端并成功 那么浏览器就会记住这个用户名和密码 然后以后每次访问都会拿着这个用户信息帮我们访问服务端 那么这个用户信息就是cookie 用户信息不一定是用户名和密码只要是能证明是自己就可以''' # session ''' 浏览器保存的cookie是明文的 那么这个时候就不安全 如果别人拿到你的用户名和密码 那么别人就可以在自己的浏览器访问服务端 这样数据就泄露了 而session就可以解决这个问题 当我们朝服务端发送请求并成功 那么服务端就会返回一个随机的字符串给浏览器 浏览器就会保存该字符串 服务端会把用户信息与该随机字符串绑定保存到服务端 该字符串就是令牌 然后浏览器就会拿着这个令牌向服务端访问 服务端会根据该令牌校验'''
二、django操作cookie
# 如果想要让客户端浏览器保存cookie需要HttpResonse对象调用方法 return HttpResponse() return render() return redirect() return JsonRepsonse() # 之前我们已经知道三板斧是直接或间接继承了HttpResponse对象所以使用三板斧没问题 JsonResponse其实也是继承了HttpResponse对象 # 想要操作cookie需要变形 obj = HttpResponse() # 产生HttpResponse对象 obj.操作cookie的方法 # 使用该对象操作cookie方法 return obj # 返回该对象
1.代码实现
# 我们可以编写一个登入程序 def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'jason' and password == '123': obj = HttpResponse('登入成功') obj.set_cookie('jason', '123') # 就可以把该cookie保存到浏览器上 return render(request, 'login.html')
2.编写一个装饰器
# 就比如现在我想编写一些只有登入的用户才能使用功能 def index(request): return HttpResponse('登入用户才能查看index功能') def func(request): return HttpResponse('登入用户才能查看func功能') def home(request): return HttpResponse('登入用户才能查看home功能') # 这个时候是不管用户是否登入都可以访问这几个功能 可以根据cookie编写 def index(request): if request.COOKIES.get('name'): # 从cookies中取值 如果有值就走if分支 return HttpResponse('登入用户才能查看index功能') return redirect('/login/') # 没有就跳转到login函数 # 但是这有个小缺陷 如果函数很多 代码就会冗余了 我们可以编写一个装饰器 # 在写装饰器之前写了解几个知识点 print(request.path) # 获取网址后缀 print(request.path_info) # 获取网址后缀 print(request.get_full_path()) # 获取网址后缀携带的数据也能获取 def login_auth(func_name): def inner(request, *args, **kwargs): if request.COOKIES.get('name'): res = func_name(request, *args, **kwargs) return res else: target_path = request.path_info return redirect(f'/login/?next={target_path}') # 因为网址?后面带一些数据是不参与匹配的所以还是会跳转到login页面 这样我们就知道用户登入之前选择的是哪个功能 return inner def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'jason' and password == '123': target_path = request.GET.get('next') # 拿到用户登入之前选择的页面 if target_path: # 有可能用户登入之前选择的就是login所以需要判断 obj = redirect(target_path) else: obj = redirect('/login/') obj.set_cookie('name', 'jason') return obj return render(request, 'get_cookie.html') @login_auth def index(request): return HttpResponse('登入用户才能查看index功能') @login_auth def func(request): return HttpResponse('登入用户才能查看func功能') @login_auth def home(request): return HttpResponse('登入用户才能查看home功能')
三、django操作session
'''请求来之后服务端产生随机字符串并发送给客户端保存 服务端存储随机字符串与用户信息的对应关系 之后客户端携带随机字符串 服务自动校验 而服务端保存用户信息与令牌的关系是保存到数据库中的 而django在执行数据库迁移命令的时候会自带一个django_session表'''
session_key 是存储令牌
session_data 是存储用户信息
expire_date 是存储session的失效时间 一旦失效用户就只能再次发送请求
1.代码实现
def set_session(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'jason' and password == '123': request.session['jason'] = '123' # 跟cookie设置不一样 return HttpResponse('登入成功') return render(request, 'get_cookie.html')
1.django默认的session失效时间14天 2.客户端会接收到键值对 键默认是sessionid值是加密的随机字符串(令牌) request.session['jason'] = '123' 1.django自动产生一个随机字符串返回给客户端(对jason加密) 2.往django_session创建数据(对123加密) def get_session(request): print(request.session.get('jason')) # 123 return HttpResponse('获取成功') # 获取session的用户信息是request.session.get('jason')
request.session.get('name') 1.自动从请求中回去sessionid对应的随机字符串 2.拿着随机字符串去django_session中匹配数据 3.如果匹配上还会自动解密数据并展示 session的存储位置可以有五种模式 数据库 缓存数据库 文件 缓存+数据库 加密 session其他操作 # 删除当前会话的所有Session数据 request.session.delete() # 删除当前的会话数据并删除会话的Cookie。 request.session.flush() # 设置会话Session和Cookie的超时时间 request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
4.django中间件
# django中间件类似于django的门户 所有的请求来和响应走都必须经过中间件 # django默认自带七个中间件 每个中间件都有各自负责的功能 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',] # django中间件除了默认的之外 还支持自定义中间件(无限) # django中间件使用场景 全局相关的功能: 全局用户身份校验 全局用户黑名单校验 全局用户访问频率校验 django自定义中间件中可以有五个方法 process_request process_response process_view process_template_response process_exception
然后我们可以查看这个五个方法是这么玩的
# 如果我们想要自定义中间件我们需要先操作一些固定的操作 # 1.首先我们需要在应用下创建一个空的文件夹 # 2.然后在文件夹中写个空的py文件 # 3.然后就可以在该py文件中编写中间件代码 # 4.最后在匹配文件中创建即可 from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class MyMdd(MiddlewareMixin): def process_request(self, request): print(request) # 配置文件 MIDDLEWARE = [ 'app01.utils.myadd.MyMdd']
1.process_request
# 配置文件 MIDDLEWARE = [ 'app01.utils.myadd.MyMdd1', 'app01.utils.myadd.MyMdd2'] # 中间件 class MyMdd1(MiddlewareMixin): def process_request(self, request): print('MyMdd1 process_request') class MyMdd2(MiddlewareMixin): def process_request(self, request): print('MyMdd2 process_request') # 视图函数 def get_md(request): print('from get_md') return HttpResponse("你好啊") '''1.请求来的时候会按照配置文件中注册了的中间件 从上往下依次执行每一个中间件里面的process_request方法 如果没有则直接跳过 然后在配置文件中 MyMdd1在MyMdd2的上面所以会先执行MyMdd1中间件 然后因为我的中间件和试图函数都是执行打印操作所以
运行结果为: MyMdd1 process_request MyMdd2 process_request from get_md 页面上展示: 你好啊''' '''2.该方法如果返回了HttpResonse对象 那么请求不会再往后执行 原路返回 就是如果中间件返回了一个HttpResponse对象那么之后的中间件和视图函数都不会执行 直接原路返回 ''' class MyMdd1(MiddlewareMixin): def process_request(self, request): print('MyMdd1 process_request') return HttpResponse("有内鬼 终止交易") class MyMdd2(MiddlewareMixin): def process_request(self, request): print('MyMdd2 process_request') 只有结果: MyMdd1 process_request 页面展示: 有内鬼 终止交易
2.process_response
# 配置文件 MIDDLEWARE = [ 'app01.utils.myadd.MyMdd1', 'app01.utils.myadd.MyMdd2'] # 中间件 class MyMdd1(MiddlewareMixin): def process_request(self, request): print('MyMdd1 process_request') def process_response(self, request, response): # 有response参数就必须要返回 print('MyMdd1 process_response') return response class MyMdd2(MiddlewareMixin): def process_request(self, request): print('MyMdd2 process_request') def process_response(self, request, response): print('MyMdd2 process_response') # 视图函数 def get_md(request): print('from get_md') return HttpResponse("你好啊") ''' 1.响应走的时候会按照配置文件中注册了的中间件 从下往上一次执行每一个中间件里面的process_response方法 没有没有则直接跳过 因为在配置文件中 MyMdd2在MyMdd1的下面所以返回的时候会先执行MyMdd2中间件 因为中间件和视图函数都是执行打印操作所以运行结果为: MyMdd1 process_request MyMdd2 process_request from get_md MyMdd2 process_response MyMdd1 process_response 页面上展示:你好啊''' ''' 2.该方法有两个形参request和response 并且默认情况下应该返回response 因为试图函数都会返回一个HttpResponse对象 然后经过中间件被中间件的response接收并返回 所以必须要返回response 如果不返回那么就会报错 不过是process_response还是其他中间件 只要是有response参数都必须返回''' '''3.该方法也可以自己返回HttpResponse对象 如果该方法不返回HttpResponse对象 那么页面上展示的就是视图函数的HttpResponse对象 但是如果该方法返回了一个HttpResponse对象那么页面就只写该中间件返回的HttpResponse对象''' class MyMdd1(MiddlewareMixin): def process_request(self, request): print('MyMdd1 process_request') def process_response(self, request, response): print('MyMdd1 process_response') return HttpResponse('不好 中途调包可') class MyMdd2(MiddlewareMixin): def process_request(self, request): print('MyMdd2 process_request') def process_response(self, request, response): print('MyMdd2 process_response') return response def get_md(request): print('from get_md') return HttpResponse("你好啊") 运行结果: MyMdd1 process_request MyMdd2 process_request from get_md MyMdd2 process_response MyMdd1 process_response 页面上展示:不好 中途调包可 ps:如果请求的过程中process_request方法直接返回了HttpResponse对象那么会原地执行同级别process_response方法返回 class MyMdd1(MiddlewareMixin): def process_request(self, request): print('MyMdd1 process_request') return HttpResponse('有内鬼 终止交易') def process_response(self, request, response): print('MyMdd1 process_response') return response class MyMdd2(MiddlewareMixin): def process_request(self, request): print('MyMdd2 process_request') def process_response(self, request, response): print('MyMdd2 process_response') return response def get_md(request): print('from get_md') return HttpResponse("你好啊") 运行结果: MyMdd1 process_request MyMdd1 process_response 页面上展示:有内鬼 终止交易 # 就是一旦process_request返回了一个HttpResponse对象 只会执行改方法和和同级别的process_response方法 后续的中间件和视图函数都不会 执行
3.process_view
# 当路由匹配成功之后 执行视图函数之前 自动触发 # 中间件 class MyMdd1(MiddlewareMixin): def process_request(self, request): print('MyMdd1 process_request') def process_response(self, request, response): print('MyMdd1 process_response') return response def process_view(self, request, view_func, view_args, view_kwargs): print('MyMdd1 process_view') class MyMdd2(MiddlewareMixin): def process_request(self, request): print('MyMdd2 process_request') def process_response(self, request, response): print('MyMdd2 process_response') return response def process_view(self, request, view_func, view_args, view_kwargs): print('MyMdd2 process_view') # 视图函数 def get_md(request): print('from get_md') return HttpResponse("你好啊") # 运行结果 MyMdd1 process_request MyMdd2 process_request MyMdd1 process_view MyMdd2 process_view from get_md MyMdd2 process_response MyMdd1 process_response 页面上展示:你好啊
4.process_excption
# 就是视图函数报错的时候就会自动触发该中间件
5.process_template_response
# 当视图函数返回的数据对象中含有render属性对应render函数才会触发 def get_md(request): print('from get_md') def render(): return HttpResponse("hahaha") obj = HttpResponse('嘿嘿嘿') obj.render = render return obj # 只有当返回的HttpResponse对象中含有render属性就会自动触发该中间件
更多知识点:https://www.cnblogs.com/Dominic-Ji/p/9229509.html
五、作业
1.使用session编写装饰器
def login_auth(func): def inner(request, *args, **kwargs): if request.session.get('name'): res = func(request, *args, **kwargs) return res else: target_path = request.path_info return redirect(f'/login/?next={target_path}') return inner def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if models.User.objects.filter(name=username, pwd=password): request.session['name'] = username target_path = request.GET.get('next') if target_path: return redirect(target_path) else: return redirect('/home/') else: return HttpResponse('用户名或密码错误') return render(request, 'get_cookie.html') @login_auth def index(request): return HttpResponse('登入用户才能查看index功能') @login_auth def func(request): return HttpResponse('登入用户才能查看func功能') @login_auth def home(request): return HttpResponse('登入用户才能查看home功能')