Bootstrap

10.Django之中间件

1. Django请求生命周期

image-20210823222842293

2. 中间键

中间键是django的门户.
1. 请求来的时候需要先进过中间件才能到达真正的django后端。
2. 响应走的时候也需要经过中间件才能发送出去.
# 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',
]
执行顺序: 按照配置文件中注册了的中间件从下往上依次经过。
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
# 0. 导入视图层
from app01 import views


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 1. 自定义中间件路由
    url(r'^index', views.index)
]
3.2 视图层
# 0. 导入django 三板斧
from django.shortcuts import render, redirect, HttpResponse

# Create your views here.


# 1. 自定义中间
def index(request):
    # 1.0 视图函数内容
    print('执行index视图函数')
    # 1.1 返回响应结果
    return HttpResponse('index页面')
3.3 定义中间键
0. 在app01下新建文件夹mymidd.
1. 在mymidd目录中写类
# 0. 导入中间键需要的类
from django.utils.deprecation import MiddlewareMixin
# 1. 导入Django 三板斧
from django.shortcuts import render, redirect, HttpResponse


# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):
	...
3.4 注册中间件
在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',
    # 注册自定义的中间键
    'app01.mymidd.midd.MyMiddleware1'
]
3.5 请求处理
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):
	
    # 2.1 定义 process_request 请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')
在浏览器中输入: 127.0.0.1:8000/index
先执行请求处理在进入到视图函数.

20220321195004

3.6 响应处理
1. 响应处理函数必须返回  response参数
   如果不返回或报错 'NoneType' object has no attribute 'get'
   
2. 返回自定义的 response参数
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')


    # 2.2 响应处理函数, 必须接收 request 与 response, 必须返回 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 返回  response参数
        return response
先执行请求处理在进入到视图函数在经过响应处理.

image-20220321001006672

response 是视图函数执行之后返回的信息
在响应处理函数中替换为自己定义的信息
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义 process_request 请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')
        # 2.1.1 返回response请求响应
        # return HttpResponse('ok')

    # 2.2 定义 process_response  响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 返回  自定义信息
        return HttpResponse('自定义返回信息')
在浏览器中输入: 127.0.0.1:8000/index

image-20220321001527109

3.7 中间件执行顺序
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义 process_request 请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')
        # 2.1.1 返回response请求响应
        # return HttpResponse('ok')

    # 2.2 定义 process_response  响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 必须返回  response对象
        return response


# 3. 自定义中间键2
class MyMiddleware2(MiddlewareMixin):

    # 3.1 定义 process_request 请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 3.1.0 自定义规则
        print('自定义中间件2 请求处理')


    # 3.2 定义 process_response  响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 3.2.0 自定义规则
        print('自定义中间件2 响应处理')
        # 3.2.1 必须返回  response对象
        return response
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.mymidd.midd.MyMiddleware1',
    'app01.mymidd.midd.MyMiddleware2'
]
在浏览器中输入: 127.0.0.1:8000/index

中间件的执行顺序
请求时:注册列表中从上往下依次次执行
响应时:注册列表中从下往上依次次执行

image-20220320233829000

3.8 研究点
如果在process_request方法就已经返回了HttpResponse对象,
直接走同级别的process_reponse,再按顺序走其他中间件的process_reponse

image-20220321002448896

# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')


    # 2.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 必须返回  response
        return response


# 3. 自定义中间键2
class MyMiddleware2(MiddlewareMixin):

    # 3.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 3.1.0 自定义规则
        print('自定义中间件2 请求处理')
        # 3.1.1 返回 response对象
        return HttpResponse('xxx信息')

    # 3.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 3.2.0 自定义规则
        print('自定义中间件2 响应处理')
        # 3.2.1 必须返回  response
        return response


# 4. 自定义中间键3
class MyMiddleware3(MiddlewareMixin):

    # 4.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 4.1.0 自定义规则
        print('自定义中间件3 请求处理')

    # 4.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 4.2.0 自定义规则
        print('自定义中间件3 响应处理')
        # 4.2.1 必须返回  response
        return response
    # 注册自定义的中间键
    'app01.mymidd.midd.MyMiddleware1',
    'app01.mymidd.midd.MyMiddleware2',
    'app01.mymidd.midd.MyMiddleware3',
在第二个中间件的请求处理中中直接返回response对象,
先走自己同级的响应处理,在按顺序执行其他中间件的响应处理.

image-20220321003040611

将同级的响应处理注销,按响应处理的执行顺序返回.

image-20220321003155972

3.9 视图响应
路由匹配成功之后执行视图函数之前,会自动执行中间件里面的process_view 方法.
必须接收request 参数, 
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')


    # 2.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 必须返回  response
        return response

	# 2.3 定义   视图过程 必须接收request 
    def process_view(self, request, view_name, *args, **kwargs):
        print(request, view_name, *args, **kwargs)
    # 注册自定义的中间键
    'app01.mymidd.midd.MyMiddleware1',
    # 'app01.mymidd.midd.MyMiddleware2',
    # 'app01.mymidd.midd.MyMiddleware3',

image-20220321004939570

view_name 参数拿到视图函数的名称 可以加括号调用函数.

image-20220321005447837

3.10 模板处理
返回的HttpResponse对象有render属性的时候才会触发process_template_response 处理模板.
必须接收 request response 参数
必须返回 response 参数
# 1. 自定义中间
def index(request):
    # 1.0 视图函数内容
    print('执行index视图函数')
    # 1.1 HttpResponse 对象
    http_response_obj = HttpResponse('index页面')

    # 1.2 定义render函数
    def render():
        print('index中的render')
        return HttpResponse('index中的render的返回信息')

    # 1.3 为 HttpResponse 对象添加render 属性
    http_response_obj.render = render

    # 1.4 返回 HttpResponse 对象
    return http_response_obj
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')


    # 2.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 必须返回  response
        return response

    # 2.3 定义   视图过程 必须接收request
    def process_view(self, request, view_name, *args, **kwargs):
        print(self, request, view_name, *args, **kwargs)
        view_name(request)

    # 2.4 定义  模板响应
    def process_template_response(self, request, response):
        print('自定义中间件1 模板响应')
        return response

image-20220321012836281

3.11 进程异常
当视图函数中出现异常的情况下触发. process_exception 方法 显示的就是报错的信息。
正常情况下无法触发.在代码中手写一个bug.
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')


    # 2.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 必须返回  response
        return response

    # 2.3 定义   视图过程 必须接收request
    def process_view(self, request, view_name, *args, **kwargs):
        print(self, request, view_name, *args, **kwargs)

    # 2.4 定义  模板响应
    def process_template_response(self, request, response):
        print('自定义中间件1 模板响应')
        return response

    # 2.5 定义  进程异常
    def process_exception(self, request, exception):
        print('自定义中间件1 进程异常信息')

        return print(exception)

image-20220321012611783

2.14 方法的执行顺序
# 1. 自定义中间
def index(request):
    # 1.0 视图函数内容
    print('执行index视图函数')
    # 1.1 HttpResponse 对象
    http_response_obj = HttpResponse()

    # 1.2 定义render函数
    def render():
        print('index中的render')
        return HttpResponse('index中的render的返回信息')

    # 1.3 为 HttpResponse 对象添加render 属性
    http_response_obj.render = render
    asdasd  # 手写bug
    # 1.4 返回 HttpResponse 对象
    return http_response_obj
# 2. 自定义中间键1
class MyMiddleware1(MiddlewareMixin):

    # 2.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 2.1.0 自定义规则
        print('自定义中间件1 请求处理')


    # 2.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 2.2.0 自定义规则
        print('自定义中间件1 响应处理')
        # 2.2.1 必须返回  response
        return response

    # 2.3 定义   视图过程 必须接收request
    def process_view(self, request, view_name, *args, **kwargs):
        print('自定义中间件1 视图过程')

    # 2.4 定义  模板响应
    def process_template_response(self, request, response):
        print('自定义中间件1 模板响应')
        return response

    # 2.5 定义  进程异常
    def process_exception(self, request, exception):
        print('自定义中间件1 进程异常信息')
        print(exception)


# 5. 自定义中间键4
class MyMiddleware4(MiddlewareMixin):

    # 4.1 定义  请求处理函数, 必须接收一个 request 参数.
    def process_request(self, request):
        # 4.1.0 自定义规则
        print('自定义中间件4 请求处理')


    # 4.2 定义   响应处理函数, 必须接收 request 与 response
    def process_response(self, request, response):
        # 4.2.0 自定义规则
        print('自定义中间件4 响应处理')
        # 4.2.1 必须返回  response
        return response

    # 4.3 定义   视图过程 必须接收request
    def process_view(self, request, view_name, *args, **kwargs):
    	print('自定义中间件4 视图过程')

    # 4.4 定义  模板响应
    def process_template_response(self, request, response):
        print('自定义中间件4 模板响应')
        return response

    # 4.5 定义  进程异常
    def process_exception(self, request, exception):
        print('自定义中间件4 进程异常信息')
        print(exception)
    # 注册自定义的中间键
    'app01.mymidd.midd.MyMiddleware1',
    # 'app01.mymidd.midd.MyMiddleware2',
    # 'app01.mymidd.midd.MyMiddleware3',
    '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

image-20220321013822424

测试时在index视图函数中删除手写 asdasd  bug

image-20220321014330643

4. csrf跨站请求伪造

钓鱼网站案例:
搭建一个跟正规网站一模一样的银行界面
用户进入到了我们的网站,用户给某个人打钱
打钱的操作确确实实是提交给了银行的系统,用户的钱也确确实实减少了
但是唯一不同的时候打钱的账户不是用户想要打的账户变成了一个莫名其妙的账户。
4.1 路由层
    # 2. 银行页面
    url(r'^bank/', views.bank)
4.2 视图层
# 2. 银行页面
def bank(request):
    # 2.0 返回 页面
    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请求的数据.
# 2. 银行页面
def bank(request):
    # 2.1 判断请求方式
    if request.method == 'POST':
        # 2.2 获取用户提交的数据
        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}元.')

    # 2.0 返回 页面
    return render(request, 'bank.html')

20220321214331

20220321214339

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属性设置值,是无法提交数据的.
现在无论给谁打款,输入的对方的账户都失效,都变成了一个你不认识的账户.

20220321215333

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校验.

20220321222902

正规的网站
    <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的标签,标签中携带随机字符串的信息,
在表单提交的时候,会先校验随机字符串是否一致.

20220321223333

5.2 ajax的csrf验证
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>我的银行</title>
    <!--0. 动态获取静态文件路径 -->
    {% load static %}
    <!--1. 导入 jQuery 文件-->
    <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请求
        $.ajax({
            url: '',
            type: 'post',
            data: {},
            success: function (args) {
                alert(args)
            }
        })
    })
</script>
</body>
</html>

20220321230816

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 %} ,
      
    
// 第三种 通用方式,直接拷贝js代码并应用到自己的html页面上即可
data:{"username":'kid'}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>我的银行</title>
    <!--0. 动态获取静态文件路径 -->
    {% load static %}
    <!--1. 导入 jQuery 文件-->
    <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()

        // 获取csrf 的随机字符串
        var uuid1 = $('[name=csrfmiddlewaretoken]').val()
        // 阻止submit按键提交数据
        event.preventDefault()

        // ajax请求
        $.ajax({
            url: '',
            type: 'post',
            // 获取csrf 的随机字符串
            data: {
                'username': username,
                'to_user': to_user,
                'money': money,
                'csrfmiddlewaretoken': uuid1
            },
            success: function (args) {
                alert(args)
            }
        })
    })
</script>
</body>
</html>
# 2. 银行页面
def bank(request):
    # 2.1 判断请求方式
    if request.is_ajax():
        if request.method == 'POST':
            # 2.2 获取用户提交的数据
            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}元.')

    # 2.0 返回 页面
    return render(request, 'bank.html')
        // 获取csrf 的随机字符串
        var uuid1 = $('[name=csrfmiddlewaretoken]').val()

        data{'csrfmiddlewaretoken': uuid1}

20220321232143

    data{'csrfmiddlewaretoken': '{{csrf_token}}'}

20220321232046

在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]);
            // Does this cookie string begin with the name we want?
            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) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});

20220322000524

6. csrf相关装饰器

1. 网站整体都不校验csrf,就单单几个视图函数需要校验。
2. 网站整体都校验csrf,就单单几个视图函数不校验。
6.1 FBV下使用
django准备的两个装饰器:
from django.views.decorators.csrf import csrf_protect,csrf_exempt
@csrf_protect  需要校验
@csrf_exempt   忽视校验  
# 配置中开启csrf校验
'django.middleware.csrf.CsrfViewMiddleware',
# 导入 模块 
from django.views.decorators.csrf import csrf_protect, csrf_exempt


# 2. 银行页面 单独关闭csrf验证
@csrf_exempt
def bank(request):
    # 2.1 判断请求方式
    if request.is_ajax():
        if request.method == 'POST':
            # 2.2 获取用户提交的数据
            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}元.')

    # 2.0 返回 页面
    return render(request, 'bank.html')

20220322001459

# 关闭csrf验证
# 'django.middleware.csrf.CsrfViewMiddleware',
# 导入 模块
from django.views.decorators.csrf import csrf_protect, csrf_exempt
# 2. 银行页面 单独开启csrf验证
@csrf_protect  
def bank(request):
    # 2.1 判断请求方式
    if request.is_ajax():
        if request.method == 'POST':
            # 2.2 获取用户提交的数据
            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}元.')

    # 2.0 返回 页面
    return render(request, 'bank.html')

20220322001844

6.2 CBV下使用
针对csrf_protect 符合之前列举使用装饰器的三种方法。
针对csrf_exempt 只能给dispatch方法加才有效。
0. 新建一个url
1. 创建一个CBV视图类
2. 设置ajax请求地址
    # 3. CBV方式
    url(r'^CsrfToken', views.CsrfToken)
from django.views import View

# CBV 视图类
class CsrfToken(View):

    # post请求 
    def post(self, request):
        return HttpResponse('post请求!')
# 配置中关闭csrf校验
# 'django.middleware.csrf.CsrfViewMiddleware',
// 设置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

# CBV 视图类
class CsrfToken(View):

    # post请求 开启csrf请求
    @method_decorator(csrf_protect)
    def post(self, request):
        return HttpResponse('post请求!')
CBV添加装饰器第二种方式 开启csrf请求
# CBV 视图类
@method_decorator(csrf_protect, 'post')
class CsrfToken(View):

    # post请求 开启csrf请求
    def post(self, request):
        return HttpResponse('post请求!')
CBV添加装饰器第三种方式 开启csrf请求
# CBV 视图类
class CsrfToken(View):
    # 自己的display 方法
    @method_decorator(csrf_protect)
    def dispatch(self, request, *args, **kwargs):
        return super(CsrfToken, self).dispatch(request,*args,**kwargs)

    # post请求 开启csrf请求

    def post(self, request):
        return HttpResponse('post请求!')
CBV添加装饰器三种方式 提交后csrf校验不通过验证正常.

20220322004958

# 配置中开启csrf校验
'django.middleware.csrf.CsrfViewMiddleware',
CBV添加装饰器第三种方式 关闭csrf请求 
# CBV 视图类
class CsrfToken(View):
    # 自己的display 方法
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(CsrfToken, self).dispatch(request,*args,**kwargs)

    # post请求 开启csrf请求
    def post(self, request):
        return HttpResponse('post请求!')
关闭成功

20220322010414

CBV添加装饰器第二种方式 关闭csrf请求 
# CBV 视图类
@method_decorator(csrf_exempt, 'post')
class CsrfToken(View):


    # post请求 开启csrf请求
    def post(self, request):
        return HttpResponse('post请求!')
CBV添加装饰器第一种方式 关闭csrf请求 
# CBV 视图类
class CsrfToken(View):

    # post请求 开启csrf请求
    @method_decorator(csrf_exempt)
    def post(self, request):
        return HttpResponse('post请求!')
CBV添加装饰器第一种方式  第二种方式  关闭csrf请求 失败

20220322010700

from django.views import View
from django.utils.decorators import method_decorator

# @method_decorator(csrf_protect,name='post')  # 针对csrf_protect 第二种方式可以
# @method_decorator(csrf_exempt,name='post')   # 针对csrf_exempt 第二种方式不可以
@method_decorator(csrf_exempt,name='dispatch')
class MyCsrfToken(View):
    # @method_decorator(csrf_protect)  # 针对csrf_protect 第三种方式可以
    # @method_decorator(csrf_exempt)   # 针对csrf_exempt 第三种方式可以
    def dispatch(self, request, *args, **kwargs):
        return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)

    def get(self,request):
        return HttpResponse('get')

    # @method_decorator(csrf_protect)  # 针对csrf_protect 第一种方式可以
    # @method_decorator(csrf_exempt)   # 针对csrf_exempt 第一种方式不可以
    def post(self,request):
        return HttpResponse('post')

7. 字符串导入模块

新建一个Python项目
在项目中创建一个 models1 目录
在目录中创建 一个b.py 文件

在项目中创建一个 a.py 文件
# b.py
name = 'kid'
# 在a.py 中使用 b.py 的中的 name变量值

from models1 import b

print(b.name)  # kid
print(b, type(b))  
# <module 'models1.b' from 'F:\\synchro\\Project\\python1\\models1\\b.py'> <class 'module'>
importlib模块
可以使用字符串的方法导入模块
import importlib

res = 'models1.b'
ret = importlib.import_module(res)  # 等于 from models1 import b
print(ret)
# <module 'models1.b' from 'F:\\synchro\\Project\\python1\\models1\\b.py'>

print(ret.name)  # kid
importlib.import_module(res)  === from models1 import b 
以点分隔
ps:在配置文件中都写字符串,该方法最小只能到py文件名(模块名)

8. 发送消息程序

8.1 普通写法
# notif.py
def wechat(content):
    print('微信通知:%s' % content)

def QQ(content):
    print('QQ通知:%s' % content)

def email(content):
    print('邮箱通知:%s' % content)
# start.py
from notif import *

def send_all(content):
    wechat(content)
    QQ(content)
    email(content)


if __name__ == '__main__':
    send_all('下班')
8.2 中间键的思路
建立一个notif目录,在文件夹下创建三个py文件。
# QQ.py
# qq平台通知
class QQ(object):
    def send(self, content):
        print(f'QQ通知{content}')
# WeChat.py
class WeChat(object):
    def send(self, content):
        print(f'微信通知{content}')
# Email.py
# 邮箱平台通知
class Email(object):
    def send(self, content):
        print(f'邮箱通知{content}')
# 在项目下创建一个settings.py 使用字符串方式导入模板
# 通知列表
NOTIF_LIST = [
    # 1. QQ功能地址
    'notif.QQ.QQ',
    # 2. 微信功能地址
    'notif.WeChat.WeChat',
    # 3. 邮箱功能地址
    'notif.Email.Email',
]
# 导入配置文件
import settings

# 导入 importlib 模块
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)
        """
        <module 'notif.QQ' from 'F:\\synchro\\Project\\python1\\notif\\QQ.py'>
        <module 'notif.WeChat' from 'F:\\synchro\\Project\\python1\\notif\\WeChat.py'>
        <module 'notif.Email' from 'F:\\synchro\\Project\\python1\\notif\\Email.py'>
        """

        # 通过反射拿到类名   getattr(模块, 类名)
        cls = getattr(module, module_name)

        # 实例化
        obj = cls()

        # 鸭子类型使用send方法
        obj.send(content)
# 在项目下创建启动文件start.py
# 导入 notify包 触发 __init__.py并执行
import notify
# 触发send_all()函数
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点开会!
;