58. django之CBV添加装饰器_中间件_csrf跨站请求

1. CBV添加装饰器的三种方式

1.1 前言

 给视图类以及视图类里的函数添加装饰器需要用到模块

from django.utils.decorators import method_decorator

1.2 代码准备

app01的views.py中定义登录函数、登录认证函数

from django.shortcuts import render, HttpResponse, redirect
from django import views
from django.utils.decorators import method_decorator
# Create your views here.

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        if username == 'avril':
            # 在登录认证已经获取目的URL的基础上,再获取目的URL
            # next内容是GET请求参数里的键值对
            next_path = request.GET.get('next')
            # 用户有可能直接访问登录页面
            if next_path:
                obj = redirect(next_path)  # 跳转到目的URL next_path,而非之前的/app01/home/
            else:
                obj = redirect('/app01/home/')  # 直接访问登录页面之后跳转到home页面
            # 设置cookie
            obj.set_cookie('sign', 'abc123')
            return obj
    return render(request, 'login.html')

def login_auth(func):
    def inner(request, *args, **kwargs):
        print(request.get_full_path())  # 获取用户登录之前的目的URL
        target_url = request.get_full_path()
        if request.COOKIES.get('sign') == 'abc123':
            res = func(request, *args, **kwargs)
            return res
        else:
            # 将目的URL当作参数传给登录函数
            return redirect(f'/app01/login/?next={target_url}')

    return inner

app01的login.html

<body>
<form action="" method="post">
    <p>username: <input type="text" name="username"></p>
    <input type="submit">
</form>
</body>

项目的urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/login/', views.login),
    path('app01/cbvde/', views.LoginView.as_view()),
]

1.3 方式一:给视图类里的函数添加装饰器 

以上views.py继续定义视图类

class LoginView(views.View):
    @method_decorator(login_auth)
    def get(self, request):
        return HttpResponse("from视图类的get函数")

    def post(self, request):
        return HttpResponse("from视图类的post函数")

打开浏览器,直接访问视图类LoginView的路由app01/cbvde/时,是get请求

get函数有装饰器login_auth,因此先进行登录验证,此时未登录跳转到login页面

image

输入用户名avril点击提交,跳转到目的URL  app01/cbvde/

image

1.4 方式二:给视图类添加装饰器

直接加在视图类上,method_decorator必须传name关键字参数

与方式一效果一样

@method_decorator(login_auth, name='get')
class LoginView(views.View):
    def get(self, request):
        return HttpResponse("from视图类的get函数")

    def post(self, request):
        return HttpResponse("from视图类的post函数")

如果get函数与post函数都需要登录验证就写两个装饰器

@method_decorator(login_auth, name='get')
@method_decorator(login_auth, name='post')
class LoginView(views.View):

    def get(self, request):
        return HttpResponse("from视图类的get函数")

    def post(self, request):
        return HttpResponse("from视图类的post函数")

1.5 方式三:重写dispatch函数

在视图类中重写dispatch函数

这种方法会给视图类中所有的函数都加上装饰器

class LoginView(views.View):
    @method_decorator(login_auth)
    def dispatch(self, request, *args, **kwargs):
        super().dispatch(request, *args, **kwargs)

    def get(self, request):
        return HttpResponse("from视图类的get函数")

    def post(self, request):
        return HttpResponse("from视图类的post函数")

2. django中间件

2.1 请求生命周期图

image

Django中间件是Django的门户
  请求发来的时候需要先经过中间件才能到达Django后端
  响应返回的时候,最后也需要经过中间件返回发送出去

2.2 Django中间件介绍

[1] 概念

Django中间件是一个轻量级、可重用的组件,用于处理Django请求和响应的过程。

它提供了对请求和响应进行全局处理的机制,可以在请求达到视图之前进行预处理或在响应返回给客户端之前进行后处理。

中间件是按照顺序依次执行的,每个中间件都可以对请求和响应进行修改、补充或处理。

在Django的settings.py配置文件中,通过MIDDLEWARE设置来定义中间件的顺序。

[2] 作用

认证和授权:

中间件可以在请求到达视图之前进行用户认证和权限验证,确保只有经过授权的用户才能访问敏感资源。
请求和响应处理:

中间件可以在请求到达视图之前对请求进行预处理

例如添加请求头信息、检查请求参数的合法性等操作。
同时,在视图函数返回响应给客户端之前,中间件还可以对响应进行后处理

例如添加额外的响应头、包装响应数据等操作。
异常处理:

中间件还可以捕获视图函数中可能抛出的异常,并做相应的处理
例如记录异常日志、返回自定义错误信息等。
性能优化:

通过中间件,可以对请求进行性能监测、缓存处理、压缩响应等操作,提升网站的整体性能。

[3] 七个中间件的作用

django自带七个中间件, 以列表的形式存放在settings.py的MIDDLEWARE

(1)SecurityMiddleware
django.middleware.security.SecurityMiddleware:
安全中间件负责处理与网站安全相关的任务
例如设置HTTP头部,防止跨站脚本攻击(XSS),点击劫持等。
它可以通过配置自定义安全策略来确保网站的安全性。
(2)SessionMiddleware
django.contrib.sessions.middleware.SessionMiddleware:
会话中间件负责处理用户会话的创建之间存储和检索用户数据。
它基于浏览器提供的Cookie或URL传递的会话ID进行会话跟踪,并将会话数据存储在后端数据库或缓存中,以实现用户状态的跨请求保持。
(3)CommonMiddleware
django.middleware.common.CommonMiddleware:
通用中间件提供了一些常见而关键的HTTP请求处理功能
例如,根据请求的HTTP头信息设置语言、时区等。
此外,它还处理静态文件的serving,包括收集静态文件,为其生成URL,并在开发模式下提供静态文件的serving。
(4)CsrfViewMiddleware
django.middleware.csrf.CsrfViewMiddleware:
CSRF(Cross-Site Request Forgery)中间件用于防止跨站请求伪造攻击。
它在每个POST请求中验证一个CSRF标记,确保请求是通过合法的表单提交得到的,从而保护用户免受恶意站点的攻击。
(5)AuthenticationMiddleware
django.contrib.auth.middleware.AuthenticationMiddleware:
认证中间件负责处理用户身份认证相关的任务
例如将认证信息关联到请求对象上,为每个请求提供一个user对象,以便在请求处理过程中轻松地获取和使用用户身份信息。
(6)MessageMiddleware
django.contrib.messages.middleware.MessageMiddleware:
消息中间件用于在请求处理过程中存储和传递临时的、一次性的用户消息。
它允许在HTTP重定向之间跨请求传递消息,例如成功或错误提示,以改善用户体验。
(7)XFrameOptionsMiddleware
django.middleware.clickjacking.XFrameOptionsMiddleware:
点击劫持中间件用于防止页面被嵌入到其他网站中,从而提供一定的点击劫持保护。
它通过设置X-Frame-Options HTTP头部来限制页面的显示方式,从而防止恶意网页通过iframe等方式嵌入当前网页。

2.3 自定义中间件

[1] 自定义中间件的函数

django提供5个可以自定义中间件的函数

  process_request
  process_response
  process_view
  process_template_response
  process_exception

[2] 方法

应用app01下创建一个任意名称的文件夹

在该文件夹内创建一个任意名称的py文件

在该py文件内编写中间件类

settings.py中注册

 

应用app01里新建defmiddleware文件夹,文件夹里新建defmiddleware.py文件,py文件里自定义中间件类

image

 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.defmiddleware.defmiddleware.DefMiddleware2',
    'app01.defmiddleware.defmiddleware.DefMiddleware1',
]

[3] process_request函数使用

项目的urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/usual/', views.usual),
]

app01的views.py

def usual(request):
    print('from视图函数usual')
    return HttpResponse('from视图函数usual')

访问路由/app01/usual/,查看后端打印

image

image

 

给process_request加上返回值

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, render, redirect

class DefMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware1的process_request')
        return HttpResponse('自定义中间件DefMiddleware1的process_request的返回值')

class DefMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware2的process_request')
访问路由/app01/usual/,视图函数的print不会打印,视图函数的Httpresponse也不会在前端展示

image

image

[4] process_response函数使用

(1) 返回默认response

settings.py

MIDDLEWARE = [
    ...'app01.defmiddleware.defmiddleware.DefMiddleware1',
    'app01.defmiddleware.defmiddleware.DefMiddleware2',
]

自定义中间件

class DefMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware1的process_request')

    def process_response(self, request, response):
        print('from自定义中间件DefMiddleware1的process_response')
        return response  # response就是视图函数返回给客户端的数据

class DefMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware2的process_request')

    def process_response(self, request, response):
        print('from自定义中间件DefMiddleware2的process_response')
        return response  # response就是视图函数返回给客户端的数据

访问路由/app01/usual/

image

image

(2) 返回自定义HttpResponse 对象

class DefMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware1的process_request')

    def process_response(self, request, response):
        print('from自定义中间件DefMiddleware1的process_response')
        return response  # response就是视图函数返回给客户端的数据

class DefMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware2的process_request')

    def process_response(self, request, response):
        print('from自定义中间件DefMiddleware2的process_response')
        return HttpResponse('自定义中间件DefMiddleware2的process_response的自定义返回值')

访问路由/app01/usual/

image

image

[5] process_view函数使用 

MIDDLEWARE = [
   ...'app01.defmiddleware.defmiddleware.DefMiddleware1',
    'app01.defmiddleware.defmiddleware.DefMiddleware2',
]

自定义中间件

class DefMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware1的process_request')

    def process_response(self, request, response):
        print('from自定义中间件DefMiddleware1的process_response')
        return response  # response就是视图函数返回给客户端的数据

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('from自定义中间件DefMiddleware1的process_view')

class DefMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('from自定义中间件DefMiddleware2的process_request')

    def process_response(self, request, response):
        print('from自定义中间件DefMiddleware2的process_response')
        return response  # response就是视图函数返回给客户端的数据

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('from自定义中间件DefMiddleware2的process_view')

image

image

[6] 自定义中间件函数总结

(1) process_request

请求到达时会从上往下依次运行settings.py中注册了的中间件里面的process_request函数

如果中间件没有定义process_request,则跳过这个中间件

如果process_request定义了返回值(render, HttpResponse, redirect),那么请求将不再继续运行,而是直接原路返回(校验失败不允许访问)

(2) process_response

请求结束并且响应返回到客户端之前,需要调用每一个中间件里面的 process_response 函数。

该函数有两个额外的参数:request、response。
process_response 函数被调用的顺序是按照配置文件中注册的中间件从下往上依次运行

如果没有定义,则跳过该中间件,校验下一个。

process_response接收一个HttpRequest对象和HttpResponse对象作为参数,并且必须返回一个HttpResponse对象:默认是response、支持自定义。

如果process_response返回了自定义HttpResponse,那么响应会替换为该HttpResponse数据,而不再是视图函数的HttpResponse数据

如果在第一个 process_request 方法就已经返回了 HttpResponse 对象,那么响应被返回的时候是经过所有的中间件里面的 process_response 方法还是会运行其它?

会直接运行同级别的 process_response 方法 ,然后直接返回

(3) process_view

view_func  目标视图函数的名称

view_args  传给视图函数的位置参数

view_kwargs   传给视图函数的关键字参数 

路由匹配成功之后,视图函数运行之前,会自动运行中间件里面的该方法

顺序是按照配置文件中注册的中间件从上往下

3. csrf跨站请求

3.1 csrf跨站请求伪造介绍

[1] 概念

CSRF(Cross-Site Request Forgery)跨站请求伪造是一种常见的网络攻击方式。
  攻击者通过诱导用户访问恶意网站或点击恶意链接
  将恶意请求发送到目标网站上
  利用用户在目标网站中已登录的身份来执行某些操作
  从而达到攻击的目的。

[2] 举例

假设用户在一家网银网站上登录账户,然后继续浏览其他网页。
同时,攻击者通过电子邮件等方式向用户发送了一封包含恶意链接的邮件。
当用户点击该链接时,潜在的威胁就会变得非常现实。
该链接指向一个由攻击者操纵的网站,该网站上的恶意代码会自动向网银网站发送一个请求,请求转账到攻击者的账户。
由于用户在网银网站中已经登录,所以该请求会被认为是合法的,这样攻击者就可以成功地进行转账操作。

[3] 保护措施

(1)  使用CSRF令牌
  在用户的请求中添加随机生成的令牌,并将该令牌保存在用户会话中。
  每次提交请求时都会验证该令牌,以确保请求是合法的。
(2)  启用SameSite属性:
  将Cookie的SameSite属性设置为Strict或Lax,以限制跨站请求。
  这可以在一定程度上缓解CSRF攻击。
(3)  严格验证请求来源
  服务器端可以验证请求的来源是否为预期的网站域名
  例如检查HTTP Referer头部。
(4)  使用验证码
  在敏感操作(如转账、更改密码等)上使用验证码
  增加用户身份验证的防护。

3.2 跨域请求伪造实际案例

[1] 钓鱼网站

搭建一个类似正规网站的页面
用户点击网站链接,给某个用户打钱
打钱的操作确确实实提交给了中国银行的系统,用户的钱也确实减少
但是唯一不同的是,账户打钱的账户不是用户想要打钱的目标账户,变成了其他用户
[2] 内部本质
在钓鱼网站的页面针对对方账户,只给用户提供一个没有name属性的普通input框
然后在内部隐藏一个已经写好带有name属性的input框
[3]如何避免
csrf跨域请求伪造校验
网站在给用户返回一个具有提交数据功能的页面的时候会给这个页面加一个唯一标识
当这个页面后端发送post请求的时候,后端会先校验唯一标识
  如果成功则正常执行
  如果唯一标识不符合则拒绝连接(403 forbidden)

3.3 csrf校验

[1] 概念

csrf校验是一种用于防止跨站请求伪造(Cross-Site Request Forgery)攻击的安全措施。

[2] CSRF校验策略

(1)添加CSRF Token字段
在form表单中添加一个隐藏字段,用于存储CSRF Token的值。
后端服务器在渲染表单时生成一个CSRF Token,并将其存储在会话中或者以其他方式关联到当前用户。
当用户提交表单时,前端将CSRF Token的值包含在请求中。
后端在验证表单数据时,检查请求中的CSRF Token是否与存储的Token匹配,如果不匹配,则拒绝请求。
(2)设置Cookie
后端服务器在渲染表单时,在客户端设置一个包含随机生成的CSRF Token的Cookie。
当用户提交表单时,表单数据会被一同发送到服务器,并自动包含该Cookie。
后端在验证表单数据时,检查请求中的CSRF Token是否与Cookie中的值匹配,如果不匹配,则拒绝请求。
(3)双重Cookie校验
后端服务器在渲染表单时,在Cookie中设置一个随机生成的CSRF Token,并将其存储在会话中或以其他方式关联到当前用户。
当用户提交表单时,表单数据会被一同发送到服务器,请求头或请求参数中携带一个包含CSRF Token的自定义字段。
后端在验证表单数据时,同时检查请求中的CSRF Token和Cookie中的值是否匹配,如果不匹配,则拒绝请求。

3.4 模拟csrf

[1] 正规网站

项目路由

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/usual/', views.usual),
]

app01的views.py

def usual(request):
    if request.method == 'POST':
        self_count = request.POST.get('self_count')
        target_count = request.POST.get('target_count')
        money = request.POST.get('money')
        return HttpResponse(f'{self_count}给{target_count}转了{money}')

    return render(request, 'usual.html')

app01的usual.html

<body>
<h2>correct web</h2>
<form action="" method="post">
    <p>当前账户:<input type="text" name="self_count"></p>
    <p>目标账户:<input type="text" name="target_count"></p>
    <p>转账金额:<input type="text" name="money"></p>
    <input type="submit">
</form>
</body>

image

image

[2] 钓鱼网站

新建一个django项目dj07

将dj07的端口修改为8001

image

项目的路由

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/usual/', views.usual),
]

app01的views.py

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

app01的usual.html,指向正规网站的端口8000

<body>
    <h2>fake web</h2>
    <form action="http://localhost:8000/app01/usual/" method="post">
        <p>当前账户:<input type="text" name="self_count"></p>
        <p>目标账户:<input type="text"></p>
        <p style="display: none">目标账户:<input type="text" name="target_count" value="cris"></p>
        <p>转账金额:<input type="text" name="money"></p>
        <input type="submit">
    </form>
</body>

image

image

3.5 post请求校验csrf

[1] form表单校验CSRF

settings.py中取消注释CsrfViewMiddleware,form 标签内的最顶部的位置加上csrf_token

正规网站项目dj06的app01的usual.html

<form action="" method="post">
    {% csrf_token %}
    <p>当前账户:<input type="text" name="self_count"></p>
    <p>目标账户:<input type="text" name="target_count"></p>
    <p>转账金额:<input type="text" name="money"></p>
    <input type="submit">
</form>

页面会自动生成一个隐藏的标签

image

点击submit之后,会将隐藏标签一并提交给后端,通过该隐藏标签的value进行校验

钓鱼网站已经无法跳转到正规网站的路由

image

image

[2] ajax校验csrf

(1) 方式一:获取标签值

将usual.html的input标签类型由submit修改为button

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{% static 'jquery/jquery.js' %}"></script>
</head>
<body>
<h2>correct web</h2>
<form action="" method="post">
    {% csrf_token %}
    <p>当前账户:<input type="text" name="self_count"></p>
    <p>目标账户:<input type="text" name="target_count"></p>
    <p>转账金额:<input type="text" name="money"></p>
    <input type="button" id="d1" value="点击提交">
</form>

<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // 利用标签查找页面上的随机字符串
            data: {
                'self_count': $('input[name="self_count"]').val(),
                'target_count': $('input[name="target_count"]').val(),
                'money': $('input[name="money"]').val()
            },
            success: function (args) {
                alert(args)
            }
        })
    })
</script>
</body>
</html>

ajax只要发送post请求,都会校验csrf

image

利用标签查找获取页面上的随机字符串,键必须为csrfmiddlewaretoken

<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // 利用标签查找页面上的随机字符串
            data: {
                'self_count': $('input[name="self_count"]').val(),
                'target_count': $('input[name="target_count"]').val(),
                'money': $('input[name="money"]').val(),
                'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val()
            },
            success: function (args) {
                alert(args)
            }
        })
    })
</script>

image

(2) 方式二:模板语法取值

"csrfmiddlewaretoken": "{{ csrf_token }}"

<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // 利用标签查找页面上的随机字符串
            data: {
                'self_count': $('input[name="self_count"]').val(),
                'target_count': $('input[name="target_count"]').val(),
                'money': $('input[name="money"]').val(),
                'csrfmiddlewaretoken': "{{ csrf_token }}"
            },
            success: function (args) {
                alert(args)
            }
        })
    })
</script>

 (3) 方式三:js文件配置

该方法只试用于ajax提交,不适用于form表单

在项目的static目录下新建csrfCheck.js文件

将以下代码放入js文件即可

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');

每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置

以下代码放在上面代码的后面

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);
    }
  }
});

 

在html页面导入该js文件即可自动解决ajax提交post数据时校验csrf_token的问题

app01的templates的usual.html中使用该js文件,前提是settings.py中配置了STATICFILES_DIRS = ['static']

导入该js文件之前,需要先导入jQuery

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{% static 'jquery/jquery.js' %}"></script>
</head>
<body>
<h2>correct web</h2>
<form action="" method="post">
    <p>当前账户:<input type="text" name="self_count"></p>
    <p>目标账户:<input type="text" name="target_count"></p>
    <p>转账金额:<input type="text" name="money"></p>
    <input type="button" id="d1" value="点击ajax提交">
</form>

<script src="{% static 'csrfCheck.js' %}"></script>
<script>
    $('#d1').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // 利用标签查找页面上的随机字符串
            data: {
                'self_count': $('input[name="self_count"]').val(),
                'target_count': $('input[name="target_count"]').val(),
                'money': $('input[name="money"]').val()
            },
            success: function (args) {
                alert(args)
            }
        })
    })
</script>
</body>
</html>

已经将form表单里面的csrf_token和ajax模板里面的csrf_token去掉,仍然可以进行csrf校验

image

image

3.6 csrf相关装饰器

[1] 前置准备

项目的urls.py中定义路由

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/login/', views.login),
]

app01的views.py中定义视图函数

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

app01里定义login.html

<body>
<form action="" method="post">
    <input type="submit">
</form>
</body>

settings.py中不注释中间件CsrfViewMiddleware

form表单点击提交时,报错403

image

image

[2] csrf装饰器概念

(1) csrf_protect装饰器

开启csrf校验

csrf_protect装饰器用于需要进行CSRF保护的视图函数或类视图。
当一个视图被csrf_protect装饰器修饰时,Django会对该视图接收到的所有POST、PUT、DELETE等非安全HTTP方法的请求进行CSRF校验。
如果请求中没有有效的CSRF令牌或令牌校验失败,Django将返回403 Forbidden响应。

(2) csrf_exempt装饰器

忽略csrf校验
csrf_exempt装饰器用于不需要进行CSRF保护的视图函数或类视图。
当一个视图被csrf_exempt装饰器修饰时,Django将不会对该视图接收到的任何请求进行CSRF校验。
这个装饰器主要用于一些特殊情况,比如与第三方系统进行集成、开放API接口等。

(3) 使用装饰器

在视图函数中导入

from django.views.decorators.csrf import csrf_protect, csrf_exempt

[3] FBV中使用CSRF装饰器

(1) 使用csrf_exempt

对以上的视图函数使用csrf_exempt装饰器,即忽略csrf校验

from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt, csrf_protect

# Create your views here.

@csrf_exempt
def login(request):
    return render(request, 'login.html')

即使settings.py中不注释中间件CsrfViewMiddleware,form表单点击提交时,不再报错

image

(2) 使用csrf_protect

settings.py中注释中间件CsrfViewMiddleware,对以上的视图函数使用csrf_protect装饰器,即开启csrf校验

from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt, csrf_protect

# Create your views here.

@csrf_protect
def login(request):
    return render(request, 'login.html')

form表单点击提交时,会进行csrf校验

image

image

(3) 总结

当没有注释csrf校验中间件时,加上@csrf_exempt 忽视校验
当注释csrf校验中间件时,加上@csrf_protect 开启校验

[4] CBV使用csrf装饰器

(1) 使用csrf_protect

方式一:给视图类里的函数加@method_decorator

app01的views.py中定义视图类

from django.shortcuts import render, HttpResponse
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django import views
from django.utils.decorators import method_decorator

# Create your views here.

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

class RegisterView(views.View):
    @method_decorator(csrf_protect)
    def post(self, request):
        return HttpResponse('from RegisterView post')

项目的urls.py中定义路由

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/login/', views.login),
    path('app01/registerview/', views.RegisterView.as_view()),
]

form表单提交至视图类

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/app01/registerview/" method="post">
    <input type="submit">
</form>
</body>
</html>

即使settings.py注释中间件CsrfViewMiddleware,login页面点击提交时朝视图类提交数据,仍然会进行csrf校验

image

image

方式二:给视图类加@method_decorator,在此基础上指定函数

方式一的基础上修改视图类部分

@method_decorator(csrf_protect, name='post')
class RegisterView(views.View):
    def post(self, request):
        return HttpResponse('from RegisterView post')

settings.py注释中间件CsrfViewMiddleware的前提下,login页面点击提交时朝视图类提交数据,仍然会进行csrf校验

image

image

方式三:重写dispatch函数,给dispatch加装饰器

class RegisterView(views.View):
    @method_decorator(csrf_protect)
    def dispatch(self, request, *args, **kwargs):
        super(RegisterView, self).dispatch(request, *args, **kwargs)

    def post(self, request):
        return HttpResponse('from RegisterView post')

重写dispatch函数,仍然能实现注释中间件CsrfViewMiddleware的前提下,进行csrf校验

image

image

(2) 使用csrf_exempt

 不注释CsrfViewMiddleware的前提下,忽略csrf校验只在重写dispatch函数时生效

class RegisterView(views.View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        super(RegisterView, self).dispatch(request, *args, **kwargs)

    def post(self, request):
        return HttpResponse('from RegisterView post')

点击提交时报以下错误

image

image

需要给dispatch函数加上返回值

class RegisterView(views.View):
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(RegisterView, self).dispatch(request, *args, **kwargs)

    def post(self, request):
        return HttpResponse('from RegisterView post')

点击提交时,跳转到了/app01/registerview/,忽略了csrf校验

image

(3) csrf_protect与csrf_exempt总结

csrf_protect符合装饰器的三种用法

csrf_exempt只在重写dispatch函数时有效

posted @ 2026-06-09 03:33  pythondjango  阅读(5)  评论(0)    收藏  举报