1. Django请求生命周期
2. 中间键
中间键是django的门户.
1. 请求来的时候需要先进过中间件才能到达真正的django后端。
2. 响应走的时候也需要经过中间件才能发送出去.
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',
]
执行顺序: 按照配置文件中注册了的中间件从下往上依次经过。
2.1 继承
所有中间键都继承 MiddlewareMixin类, 内部都定义了以后两个方法.
1. process_request 请求处理
2. process_response 处理响应
class SessionMiddleware(MiddlewareMixin):
def process_request(self, request):
···
def process_response(self, request, response):
class CsrfViewMiddleware(MiddlewareMixin):
def process_request(self, request):
···
def process_view(self, request, callback, callback_args, callback_kwargs):
···
def process_response(self, request, response):
···
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
···
3. 自定义中间件
在app应用下创建一个目录,在目录下创建一个py文件
在py文件中写自定义的类,类必须继承MiddlewareMixin
需要导入MiddlewareMixin模块。
from django.utils.deprecation import MiddlewareMixin
django支持程序员自定义中间件并且暴露五个可以自定义的方法
将类的路径以字符串的形式注册到配置文件中.使其生效.
中间件的执行顺序是按照配置文件中注册的中间件从上往下的顺序依次执行。
process_request方法: 全局相关 --> 所有限制功能
1.请求来的时候需要经过每一个中间件里面的process_request方法
2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件。
3.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行
而是直接原路返回(校验失败不允许访问...)。
process_response:
1.响应走的时候需要中间件里面的process_response方法
该方法有两个额外的参数request,response
只要在形参中遇到 接收了response参数,就必须返回response。
2.该方法必须返回一个HttpResponse对象
1.默认返回的就是形参response, 如果不返回前端就没有信息,
2.可以返回自己定义的
3.1 路由层
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index', views.index)
]
3.2 视图层
from django.shortcuts import render, redirect, HttpResponse
def index(request):
print('执行index视图函数')
return HttpResponse('index页面')
3.3 定义中间键
0. 在app01下新建文件夹mymidd.
1. 在mymidd目录中写类
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
class MyMiddleware1(MiddlewareMixin):
...
3.4 注册中间件
在settings.py中注册中间键
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'app01.mymidd.midd.MyMiddleware1'
]
3.5 请求处理
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
在浏览器中输入: 127.0.0.1:8000/index
先执行请求处理在进入到视图函数.
3.6 响应处理
1. 响应处理函数必须返回 response参数
如果不返回或报错 'NoneType' object has no attribute 'get'
2. 返回自定义的 response参数
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
先执行请求处理在进入到视图函数在经过响应处理.
response 是视图函数执行之后返回的信息
在响应处理函数中替换为自己定义的信息
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return HttpResponse('自定义返回信息')
在浏览器中输入: 127.0.0.1:8000/index
3.7 中间件执行顺序
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
class MyMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件2 请求处理')
def process_response(self, request, response):
print('自定义中间件2 响应处理')
return response
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'app01.mymidd.midd.MyMiddleware1',
'app01.mymidd.midd.MyMiddleware2'
]
在浏览器中输入: 127.0.0.1:8000/index
中间件的执行顺序
请求时:注册列表中从上往下依次次执行
响应时:注册列表中从下往上依次次执行
3.8 研究点
如果在process_request方法就已经返回了HttpResponse对象,
直接走同级别的process_reponse,再按顺序走其他中间件的process_reponse
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
class MyMiddleware2(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件2 请求处理')
return HttpResponse('xxx信息')
def process_response(self, request, response):
print('自定义中间件2 响应处理')
return response
class MyMiddleware3(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件3 请求处理')
def process_response(self, request, response):
print('自定义中间件3 响应处理')
return response
'app01.mymidd.midd.MyMiddleware1',
'app01.mymidd.midd.MyMiddleware2',
'app01.mymidd.midd.MyMiddleware3',
在第二个中间件的请求处理中中直接返回response对象,
先走自己同级的响应处理,在按顺序执行其他中间件的响应处理.
将同级的响应处理注销,按响应处理的执行顺序返回.
3.9 视图响应
路由匹配成功之后执行视图函数之前,会自动执行中间件里面的process_view 方法.
必须接收request 参数,
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
def process_view(self, request, view_name, *args, **kwargs):
print(request, view_name, *args, **kwargs)
'app01.mymidd.midd.MyMiddleware1',
view_name 参数拿到视图函数的名称 可以加括号调用函数.
3.10 模板处理
返回的HttpResponse对象有render属性的时候才会触发process_template_response 处理模板.
必须接收 request response 参数
必须返回 response 参数
def index(request):
print('执行index视图函数')
http_response_obj = HttpResponse('index页面')
def render():
print('index中的render')
return HttpResponse('index中的render的返回信息')
http_response_obj.render = render
return http_response_obj
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
def process_view(self, request, view_name, *args, **kwargs):
print(self, request, view_name, *args, **kwargs)
view_name(request)
def process_template_response(self, request, response):
print('自定义中间件1 模板响应')
return response
3.11 进程异常
当视图函数中出现异常的情况下触发. process_exception 方法 显示的就是报错的信息。
正常情况下无法触发.在代码中手写一个bug.
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
def process_view(self, request, view_name, *args, **kwargs):
print(self, request, view_name, *args, **kwargs)
def process_template_response(self, request, response):
print('自定义中间件1 模板响应')
return response
def process_exception(self, request, exception):
print('自定义中间件1 进程异常信息')
return print(exception)
2.14 方法的执行顺序
def index(request):
print('执行index视图函数')
http_response_obj = HttpResponse()
def render():
print('index中的render')
return HttpResponse('index中的render的返回信息')
http_response_obj.render = render
asdasd
return http_response_obj
class MyMiddleware1(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件1 请求处理')
def process_response(self, request, response):
print('自定义中间件1 响应处理')
return response
def process_view(self, request, view_name, *args, **kwargs):
print('自定义中间件1 视图过程')
def process_template_response(self, request, response):
print('自定义中间件1 模板响应')
return response
def process_exception(self, request, exception):
print('自定义中间件1 进程异常信息')
print(exception)
class MyMiddleware4(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件4 请求处理')
def process_response(self, request, response):
print('自定义中间件4 响应处理')
return response
def process_view(self, request, view_name, *args, **kwargs):
print('自定义中间件4 视图过程')
def process_template_response(self, request, response):
print('自定义中间件4 模板响应')
return response
def process_exception(self, request, exception):
print('自定义中间件4 进程异常信息')
print(exception)
'app01.mymidd.midd.MyMiddleware1',
'app01.mymidd.midd.MyMiddleware4',
执行顺序:
0. process_request
1. process_view
2. process_exception & process_template_response
报错就走 process_exception 不报错就走 process_template_response
3. process_response
测试时在index视图函数中手写 asdasd bug
测试时在index视图函数中删除手写 asdasd bug
4. csrf跨站请求伪造
钓鱼网站案例:
搭建一个跟正规网站一模一样的银行界面
用户进入到了我们的网站,用户给某个人打钱
打钱的操作确确实实是提交给了银行的系统,用户的钱也确确实实减少了
但是唯一不同的时候打钱的账户不是用户想要打的账户变成了一个莫名其妙的账户。
4.1 路由层
url(r'^bank/', views.bank)
4.2 视图层
def bank(request):
return render(request, 'bank.html')
4.3 前端页面
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的银行title>
head>
<body>
<div>
<h1>转账功能h1>
<form action="" method="post">
<p>username:
<input type="text" name="username" value="kid">
p>
<p>对方账户:
<input type="text" name="to_user">
p>
<p>金额:
<input type="text" name="money">
p>
<input type="submit">
form>
div>
body>
html>
4.4业务逻辑
获取post请求的数据.
def bank(request):
if request.method == 'POST':
post_obj = request.POST
username = post_obj.get('username')
to_user = post_obj.get('to_user')
money = post_obj.get('money')
return HttpResponse(f'{username} 已经给 {to_user} 转账 {money}元.')
return render(request, 'bank.html')
4.5 钓鱼网站
钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框
然后我们在内部隐藏一个已经写好name和value的input框
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的银行title>
head>
<body>
<div>
<h1>转账功能h1>
<form action="" method="post">
<p>username:
<input type="text" name="username" value="kid">
p>
<p>对方账户:
<input type="text">
<input type="text" name="to_user" value='cheater' style="display: none">
p>
<p>金额:
<input type="text" name="money">
p>
<input type="submit">
form>
div>
body>
html>
<p>对方账户:
<input type="text">
<input type="text" name="to_user" value='cheater' style="display: none">
p>
input款没有为name属性设置值,是无法提交数据的.
现在无论给谁打款,输入的对方的账户都失效,都变成了一个你不认识的账户.
5. csrf跨站请求校验
网站在给用户提供一个具有提交数据功能页面的时候,会给这个页面加一个唯一标识(随机字符串)。
当这个页面朝后端发送post请求的时候 后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)
如果成功则正常执行···
5.1 form表单csrf校验
将中间键 'django.middleware.csrf.CsrfViewMiddleware' 的注释取消。
jango.middleware.csrf.CsrfViewMiddleware 所有post访问需要校验csrf。
使用方式:在form表单中添加上内置模板语法
{% csrf_token %}
现在钓鱼网站是无法拿到服务器提供的csrf的,加上了{% csrf_token %}也没有作用.
这个时候钓鱼网站向服务器发送数据要转账,就无法过csrf校验.
正规的网站
<form action="" method="post">
{% csrf_token %}
<p>username:
<input type="text" name="username" value="kid">
p>
<p>对方账户:
<input type="text" name="to_user">
p>
<p>金额:
<input type="text" name="money">
p>
<input type="submit">
form>
{% csrf_token %}
相当于是一个inout标签
<input type="hidden" name="csrfmiddlewaretoken" value="随机字符串">
在讲过csrf中间件的时候生成一个随机字符串,在做个一个html的标签,标签中携带随机字符串的信息,
在表单提交的时候,会先校验随机字符串是否一致.
5.2 ajax的csrf验证
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的银行title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
head>
<body>
<div>
<h1>转账功能h1>
<form action="" method="post">
{% csrf_token %}
<p>username:
<input type="text" name="username" value="kid">
p>
<p>对方账户:
<input type="text" name="to_user">
p>
<p>金额:
<input type="text" name="money">
p>
<input type="submit" id="btn1">
form>
div>
<script>
$("#btn1").on('click', function () {
$.ajax({
url: '',
type: 'post',
data: {},
success: function (args) {
alert(args)
}
})
})
script>
body>
html>
5.3 ajax校验方式
提交ajax请求的data参数中没有csrf的随机字符串,是无法通过的.
ajax提交的时候先,获取input标签的value值.
<input type="hidden" name="csrfmiddlewaretoken" value="随机字符串">
data:{"csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
data:{"csrfmiddlewaretoken':'{{ csrf_token }}'}
{{ csrf_token }} 获取的是值 需要''单引号括起来, 不要写成 {% csrf_token %} ,
data:{"username":'kid'}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>我的银行title>
{% load static %}
<script src="{% static 'js/jquery-3.6.0.min.js' %}">script>
head>
<body>
<div>
<h1>转账功能h1>
<form action="" method="post">
{% csrf_token %}
<p>username:
<input type="text" name="username" value="kid">
p>
<p>对方账户:
<input type="text" name="to_user">
p>
<p>金额:
<input type="text" name="money">
p>
<input type="submit" id="btn1">
form>
div>
<script>
$("#btn1").on('click', function () {
var username = $('[name=username]').val()
var to_user = $('[name=to_user]').val()
var money = $('[name=money]').val()
var uuid1 = $('[name=csrfmiddlewaretoken]').val()
event.preventDefault()
$.ajax({
url: '',
type: 'post',
data: {
'username': username,
'to_user': to_user,
'money': money,
'csrfmiddlewaretoken': uuid1
},
success: function (args) {
alert(args)
}
})
})
script>
body>
html>
def bank(request):
if request.is_ajax():
if request.method == 'POST':
post_obj = request.POST
username = post_obj.get('username')
to_user = post_obj.get('to_user')
money = post_obj.get('money')
return HttpResponse(f'{username} 已经给 {to_user} 转账 {money}元.')
return render(request, 'bank.html')
// 获取csrf 的随机字符串
var uuid1 = $('[name=csrfmiddlewaretoken]').val()
data{'csrfmiddlewaretoken': uuid1}
data{'csrfmiddlewaretoken': '{{csrf_token}}'}
在static文件夹下建立js文件夹
在js目录中 新建 mysetup.js,复制下面官方给的代码。
在需要csrf校验的文件中导入这个文件.
<script src="{% static 'js/mysetup.js' %}"></script>
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
6. csrf相关装饰器
1. 网站整体都不校验csrf,就单单几个视图函数需要校验。
2. 网站整体都校验csrf,就单单几个视图函数不校验。
6.1 FBV下使用
django准备的两个装饰器:
from django.views.decorators.csrf import csrf_protect,csrf_exempt
@csrf_protect 需要校验
@csrf_exempt 忽视校验
'django.middleware.csrf.CsrfViewMiddleware',
from django.views.decorators.csrf import csrf_protect, csrf_exempt
@csrf_exempt
def bank(request):
if request.is_ajax():
if request.method == 'POST':
post_obj = request.POST
username = post_obj.get('username')
to_user = post_obj.get('to_user')
money = post_obj.get('money')
return HttpResponse(f'{username} 已经给 {to_user} 转账 {money}元.')
return render(request, 'bank.html')
from django.views.decorators.csrf import csrf_protect, csrf_exempt
@csrf_protect
def bank(request):
if request.is_ajax():
if request.method == 'POST':
post_obj = request.POST
username = post_obj.get('username')
to_user = post_obj.get('to_user')
money = post_obj.get('money')
return HttpResponse(f'{username} 已经给 {to_user} 转账 {money}元.')
return render(request, 'bank.html')
6.2 CBV下使用
针对csrf_protect 符合之前列举使用装饰器的三种方法。
针对csrf_exempt 只能给dispatch方法加才有效。
0. 新建一个url
1. 创建一个CBV视图类
2. 设置ajax请求地址
url(r'^CsrfToken', views.CsrfToken)
from django.views import View
class CsrfToken(View):
def post(self, request):
return HttpResponse('post请求!')
// 设置ajax请求的提交地址 前端不提交csrf的数据
url:'/CsrfToken/',
CBV添加装饰器第一种方式 开启csrf请求
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect, csrf_exempt
class CsrfToken(View):
@method_decorator(csrf_protect)
def post(self, request):
return HttpResponse('post请求!')
CBV添加装饰器第二种方式 开启csrf请求
@method_decorator(csrf_protect, 'post')
class CsrfToken(View):
def post(self, request):
return HttpResponse('post请求!')
CBV添加装饰器第三种方式 开启csrf请求
class CsrfToken(View):
@method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
return super(CsrfToken, self).dispatch(request,*args,**kwargs)
def post(self, request):
return HttpResponse('post请求!')
CBV添加装饰器三种方式 提交后csrf校验不通过验证正常.
'django.middleware.csrf.CsrfViewMiddleware',
CBV添加装饰器第三种方式 关闭csrf请求
class CsrfToken(View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(CsrfToken, self).dispatch(request,*args,**kwargs)
def post(self, request):
return HttpResponse('post请求!')
关闭成功
CBV添加装饰器第二种方式 关闭csrf请求
@method_decorator(csrf_exempt, 'post')
class CsrfToken(View):
def post(self, request):
return HttpResponse('post请求!')
CBV添加装饰器第一种方式 关闭csrf请求
class CsrfToken(View):
@method_decorator(csrf_exempt)
def post(self, request):
return HttpResponse('post请求!')
CBV添加装饰器第一种方式 第二种方式 关闭csrf请求 失败
from django.views import View
from django.utils.decorators import method_decorator
@method_decorator(csrf_exempt,name='dispatch')
class MyCsrfToken(View):
def dispatch(self, request, *args, **kwargs):
return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')
7. 字符串导入模块
新建一个Python项目
在项目中创建一个 models1 目录
在目录中创建 一个b.py 文件
在项目中创建一个 a.py 文件
name = 'kid'
from models1 import b
print(b.name)
print(b, type(b))
importlib模块
可以使用字符串的方法导入模块
import importlib
res = 'models1.b'
ret = importlib.import_module(res)
print(ret)
print(ret.name)
importlib.import_module(res) === from models1 import b
以点分隔
ps:在配置文件中都写字符串,该方法最小只能到py文件名(模块名)
8. 发送消息程序
8.1 普通写法
def wechat(content):
print('微信通知:%s' % content)
def QQ(content):
print('QQ通知:%s' % content)
def email(content):
print('邮箱通知:%s' % content)
from notif import *
def send_all(content):
wechat(content)
QQ(content)
email(content)
if __name__ == '__main__':
send_all('下班')
8.2 中间键的思路
建立一个notif目录,在文件夹下创建三个py文件。
class QQ(object):
def send(self, content):
print(f'QQ通知{content}')
class WeChat(object):
def send(self, content):
print(f'微信通知{content}')
class Email(object):
def send(self, content):
print(f'邮箱通知{content}')
NOTIF_LIST = [
'notif.QQ.QQ',
'notif.WeChat.WeChat',
'notif.Email.Email',
]
import settings
import importlib
def send_all(content):
for path_str in settings.NOTIF_LIST:
module_dir, module_name = path_str.rsplit('.', maxsplit=1)
print(module_dir, module_name)
"""
模块地址 模块名字
notif.QQ QQ
notif.WeChat WeChat
notif.Email Email
"""
module = importlib.import_module(module_dir)
print(module)
"""
"""
cls = getattr(module, module_name)
obj = cls()
obj.send(content)
import notify
notify.send_all('下班了')
启动 start.py
D:\Python\Python3.6\python.exe F:/synchro/Project/python1/statt.py
notif.QQ QQ
<module 'notif.QQ' from 'F:\\synchro\\Project\\python1\\notif\\QQ.py'>
QQ通知5点开会!
notif.WeChat WeChat
<module 'notif.WeChat' from 'F:\\synchro\\Project\\python1\\notif\\WeChat.py'>
微信通知5点开会!
notif.Email Email
<module 'notif.Email' from 'F:\\synchro\\Project\\python1\\notif\\Email.py'>
邮箱通知5点开会!