day75 中间件

本文详细解析了Django中的session原理,自定义中间件流程,以及如何利用中间件进行IP访问限制、URL过滤和防止CSRF攻击。重点讲解了CSRF_TOKEN在form表单和Ajax中的应用,以及如何将其安全地放置在cookie中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨日回顾

1 django中cookie的使用
	-增 obj.set_cookie(key,value,max_age)
    -删 obj.delete_cookie(key)
    -查 request.COOKIE.get(key)
    -改 obj.set_cookie(key,value,max_age)
2 django中session的使用(迁移数据库)
	-增 request.session['key']=value
    -del request.session['key']
    -全部删除 request.session.flush():cookie和数据库都删除   request.session.delete()只删数据库
    -查 request.session['key']
    -改 request.session['key']=value
    
    -超时时间
    
3 cbv加装饰器
	-第一种:加在类上
        from django.utils.decorators import method_decorator
        @method_decorator(login_auth,name='get')
        class UserList(views.View):
            pass
    -第二种:加在方法上
    	from django.utils.decorators import method_decorator
       
        class UserList(views.View):
            @method_decorator(login_auth)
            def get(self):
            	pass

今日内容

1 django的session原理流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qrVMuJ3M-1603457731159)(C:\Users\高雨\Desktop\python学习\前端\django的session原理.png)]

2 中间件

2.1 什么是中间件

中间件顾名思义,是介于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.

2.2 中间件有什么用

如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

Django默认的中间件:(在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下图)

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.3 自定义中间件

中间件中主要有几个方法:

process_request(self,request)

process_view(self, request, callback, callback_args, callback_kwargs)

process_template_response(self,request,response)

process_exception(self, request, exception)

process_response(self, request, response)
process_request和process_response

当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求是process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DyiGTerC-1603457698070)(C:\Users\高雨\AppData\Roaming\Typora\typora-user-images\image-20201023153553285.png)]

上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,我们可以自己写一个类,但是必须继承MiddlewareMixin

第一步:导入

from  django.utils.deprecation import MiddlewareMixin

第二步:自定义中间件

新建中间件的py文件

写一个中间件的类,类中定义方法def process_request

from django.utils.deprecation import MiddlewareMixin
# process_request
# process_response
from django.shortcuts import render,HttpResponse

class MyMiddleware1(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self,request): # request就是当请求的request对象
        print('请求来了1')
        # print(request.session)
        # 可不可以返回?必须返回HttpResponse的对象
        # return HttpResponse('我不让你访问')

    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了1')
        return response

    
class MyMiddleware2(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self,request): # request就是当请求的request对象
        print('请求来了2')
        # print(request.session)
        # 可不可以返回?
        # return HttpResponse('dsb')

    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了2')
        return response

第三步:在view中定义一个视图函数(index)

def index(request):
    print('我是视图函数')
    return HttpResponse('ok')

第四步:在settings.py的MIDDLEWARE里注册自己定义的中间件(注意注册的先后顺序)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TtbNkiyZ-1603457698095)(C:\Users\高雨\AppData\Roaming\Typora\typora-user-images\image-20201023160327584.png)]

​ 第五步:配置路由:

url(r'', views.index),

结果:

请求来了1
请求来了2
我是视图函数
请求走了2
请求走了1

**注意:**如果当请求到达请求2的时候直接不符合条件返回,即def process_request(self,request)方法添加

return HttpResponse(“Md2中断”),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:

返回Md2中断的页面,后台打印如下:

请求来了1
请求来了2
请求走了2
请求走了1

流程图如下:

y67k3n0GozKvFAnjh2DE+UerT98WieKXd8tvfwfWX35GbcUe9Y/rIA8rvfqnYsxt52PwSYehqmgffrrhDeT8tsItINr60WfIXbYGk56/HhMfnoGDzj

由此总结一下:

  1. 中间件的process_request方法是在执行视图函数之前执行的。
  2. 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
  3. 不同中间件之间传递的request都是同一个对象

多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。

process_view

process_view(self, request, view_func, view_args, view_kwargs)

该方法有四个参数

request是HttpRequest对象。

view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)

view_args是将传递给视图的位置参数的列表.

view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。

Django会在调用视图函数之前调用process_view方法。

它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

process_view(self, request, callback, callback_args, callback_kwargs)
from django.utils.deprecation import MiddlewareMixin

from django.shortcuts import render,HttpResponse

class MyMiddleware1(MiddlewareMixin):
    def process_request(self,request):
        print('请求来了1')
        # return HttpResponse("Md1中断")


    def process_response(self,request,response):
        print('请求走了1')
        return response
	
    # 路由匹配成功,视图函数执行之前执行
    def process_view(self,request, callback, callback_args, callback_kwargs):
        print('中间件1的process_view')


class MyMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('请求来了2')
        # return HttpResponse("Md2中断")

    def process_view(self,request, callback, callback_args, callback_kwargs):
        print('中间件2的process_view')

    def process_response(self, request, response):
        print('请求走了2')
        return response

结果如下:

请求来了1
请求来了2
中间件1的process_view
中间件2的process_view
我是视图函数
请求走了2
请求走了1

下图进行分析上面的过程:

pxhUVe0mZaLY86Vrxsng2b5G+pnXStfRdo9x458IaHGVnUB/itMGW55CKuZeLan/X528b7e/87C3Z8ObNUUqOfmhX+qlK9LZvE1/3wP9OvR1bjP3oNVn7qN/vV3O7Rtf15bquH+B2u2+3SpCqRs7q869n1fui+73inLaf8UJVXwwcM+MU2frxY2Ev3ETzpbfufXE4x0ve+PFGvpLaw2XzZ4/LqNp5A+U67FJcOVu6174TVi471hWw+twJjNxQ3ruSeY+M55bM+3ugXRv/+wfZ3uiWvub10vrWn6Rk0mxR7/3x2pBongfKtuozV1SHObLb1y0LEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEQl7g/wEc6TDTyRk/pAAAAABJRU5ErkJgggA=)

当最后一个中间的process_request到达路由关系映射之后,返回到中间件1的process_view,然后依次往下,到达views函数,最后通过process_response依次返回到达用户。

process_view可以用来调用视图函数:

class MyMiddleware1(MiddlewareMixin):
    def process_request(self,request):
        print('请求来了1')
        # return HttpResponse("Md1中断")


    def process_response(self,request,response):
        print('请求走了1')
        return response

    def process_view(self,request, callback, callback_args, callback_kwargs):
        print(callback)
        print(callback_args)
        print(callback_kwargs)
        res=callback(request)
        print('中间件1的process_view')
        return res


class MyMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('请求来了2')
        # return HttpResponse("Md2中断")

    def process_view(self,request, callback, callback_args, callback_kwargs):
        print('中间件2的process_view')

    def process_response(self, request, response):
        print('请求走了2')
        return response

运行结果如下:

请求来了1
请求来了2
<function index at 0x000001AAC1619B80>
()
{}
我是视图函数
中间件1的process_view
请求走了2
请求走了1

注意:process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。

process_exception

process_exception(self, request, exception)

该方法两个参数:

一个HttpRequest对象

一个exception是视图函数异常产生的Exception对象。

这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

process_exception(self, request, exception)

示例修改如下:

from django.utils.deprecation import MiddlewareMixin

from django.shortcuts import render,HttpResponse

class MyMiddleware1(MiddlewareMixin):
    def process_request(self,request):
        print('请求来了1')
        # return HttpResponse("Md1中断")


    def process_response(self,request,response):
        print('请求走了1')
        return response

    def process_view(self,request, callback, callback_args, callback_kwargs):
        # print(callback)
        # print(callback_args)
        # print(callback_kwargs)
        # res=callback(request,*callback_args,**callback_kwargs)
        print('中间件1的process_view')
        # return res


class MyMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('请求来了2')
        # return HttpResponse("Md2中断")

    def process_view(self,request, callback, callback_args, callback_kwargs):
        print('中间件2的process_view')

    def process_response(self, request, response):
        print('请求走了2')
        return response

    # 全局异常捕获,返回4开头的
    def process_exception(self,request,exception):
        print(exception)
        print('exception')
        return render(request,'error.html')

views.py

def index(request):
    print('我是视图函数')
    lalal
    return HttpResponse('ok')

结果如下

请求来了1
请求来了2
中间件1的process_view
中间件2的process_view
我是视图函数
name 'lalal' is not defined
exception
请求走了2
请求走了1

流程图如下:

当views出现错误时:

在这里插入图片描述

process_template_response(self,request,response)

该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法

class Test:
    def __init__(self,status,msg):
        self.status=status
        self.msg=msg
    def render(self):
        import json
        dic={'status':self.status,'msg':self.msg}

        return HttpResponse(json.dumps(dic))
def index(response):
    return Test(True,'测试')

总结:

1 process_request(request对象)
2 process_response(request对象,response对象)
3 多个中间件,执行顺序是什么?
	-请求来的时候从上往下执行:process_request
    -请求走的时候,从下往上执行:process_response
        
        
4 process_request可以干什么?
	-写一个中间件,不管前端用什么编码,在requset.data中都有post的数据
    -频率限制(限制某个ip地址,一分钟只能访问5次)
    -登录认证(只要没登录,重定向到login路径)、
    -记录用户访问日志(ip,时间,访问路径)
    
5 process_response可以干什么?内部有response对象
	-统一给所有(某几个路径)加cookie
    -统一给所有(某几个路径)加响应头
    
    
    
6 process_view 路由匹配成功和视图函数执行之前执行(callback就是视图函数)
    def process_view(self, request, callback, callback_args, callback_kwargs):
            # print(callback)
            # print(callback_args)
            # print(callback_kwargs)
            #
            res=callback(request)
            #
            print("中间件1的process_view")
            return res
7 process_exception 视图函数出错,会执行它(全局异常捕获)(记录日志,哪个ip地址,访问哪个路径,出的错)
    # 全局异常捕获,返回4开头的
    def process_exception(self, request, exception):
        print(exception)
        return render(request,'error.html')

2.4 中间件应用场景

1 做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

2 URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

作为延伸扩展内容,有余力的同学可以尝试着读一下以下两个自带的中间件:

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',

2.5 CSRF_TOKEN跨站请求伪造

介绍:浅谈CSRF(Cross-site request forgery)跨站请求伪造

在form表单中应用:
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit"></p>
</form>

例:黑客伪造招商银行页面(跨站请求伪造)

views

def login_auth(func):
    def inner(request, *args, **kwargs):
        # 登录校验
        name = request.session.get('name')
        if name:
            res = func(request, *args, **kwargs)
            return res
        else:
            path = request.get_full_path()
            return redirect('/login/?returnUrl=%s' % path)

    return inner


### session版登录
def login(request):
    if request.method == 'GET':

        return render(request, 'login.html')
    else:
        name = request.POST.get('name')
        password = request.POST.get('password')
        if name == 'lqz' and password == '123':
            # 写入session
            # 登录成功,重定向
            request.session['name']=name
            path = request.GET.get('returnUrl')
            if path:
                obj = redirect(path)
            else:
                obj = redirect('/index/')

            return obj
        else:
            return HttpResponse('用户名或密码错误')

@login_auth
def index(request):
    return render(request,'index.html')


@login_auth
def transfer(request):
    # /transfer/?from=lqz&to=egon&total=100
    f=request.GET.get('from')
    t=request.GET.get('to')
    total=request.GET.get('total')
    print('转账成功')
    return HttpResponse('转账成功')

def hack(request):
    return render(request,'hack.html')

路由配置

urlpatterns = [
    url(r'^index/', views.index),

    url(r'^login/', views.login),
    url(r'^transfer/', views.transfer),
    url(r'^hack/', views.hack),

login.html

<form action="" method="post">
        <p>用户名:<input type="text" name="name"></p>
        <p>密码:<input type="password" name="password"></p>
        <p><input type="submit" value="提交"></p>
</form>

index.html

<head>
    <meta charset="UTF-8">
    <title>招商银行正版</title>
</head>
<body>

<a href="/transfer/?from=lqz&to=egon&total=100">点我给egon转100块钱</a>

</body>

hack.html

<a href="/transfer/?from=lqz&to=egon&total=100">点我查看更多</a>

解决方案:django解决了csrf攻击,中间件:django.middleware.csrf.CsrfViewMiddleware

后期中间件不能注释,每次发送post请求,都需要携带csrf_token随机字符串

form表单提交 :在form表单中 {% csrf_token%}

views

def crsf_test(request):
    if request.method=='GET':
        return render(request,'csrf_test.html')
    else:
        name=request.POST.get('name')
        password=request.POST.get('password')
        print(name)
        print(password)
        return HttpResponse('登陆成功')

csrf_test.html

<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit"></p>
</form>

路由配置

url(r'^crsf_test/', views.crsf_test),
在Ajax中应用:

csrf_test.html

放在data里:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password" id="pwd"></p>
    <p><input type="submit"></p>
</form>
<button class="btn">点我</button>
</body>
<script>
    $(".btn").click(function () {
        $.ajax({
            url: '',
            type: 'post',
            data: {
                'name': $('[name="name"]').val(),
                'password': $("#pwd").val(),
                'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
            },
            success: function (data) {
                console.log(data)
            }

        })
    })
</script>
</html>
        
放在cookie里:

获取cookie:document.cookie

是一个字符串,可以自己用js切割,也可以用jquery的插件

获取cookie:$.cookie(‘csrftoken’)

设置cookie:$.cookie(‘key’,‘value’)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password" id="pwd"></p>
    <p><input type="submit"></p>
</form>
<button class="btn">点我</button>
</body>
<script>
    $(".btn").click(function () {
        var token=$.cookie('csrftoken')
        //var token='{{ csrf_token }}'
        $.ajax({
            url: '',
            headers:{'X-CSRFToken':token},
            type: 'post',
            data: {
                'name': $('[name="name"]').val(),
                'password': $("#pwd").val(),
            },
            success: function (data) {
                console.log(data)
            }

        })
    })
</script>
</html>

其它操作

全站禁用:注释掉中间件 ‘django.middleware.csrf.CsrfViewMiddleware’,

局部禁用:用装饰器(在FBV中使用)

views

from django.views.decorators.csrf import csrf_exempt,csrf_protect
# 全局启用,局部禁用(中间件不能注释,这个视图函数,已经没有csrf校验了)
@csrf_exempt
def csrf_test(request):
    if request.method=='GET':
        return render(request,'csrf_test.html')
    else:
        name=request.POST.get('name')
        password=request.POST.get('password')
        print(name)
        print(password)
        return HttpResponse('登录成功')

# 全局禁用,局部使用(前提是中间件要注释掉)
@csrf_protect
def csrf_test(request):
    if request.method=='GET':
        return render(request,'csrf_test.html')
    else:
        name=request.POST.get('name')
        password=request.POST.get('password')
        print(name)
        print(password)
        return HttpResponse('登录成功')

路由配置

url(r'crsf_test',csrf_exempt(views.csrf_test)),
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值