Rosmontis&迷迭香的博客&鹤望兰分部

Rosmontis&迷迭香的博客
僅用於Bing訪客使用,若需要最新的文章,建議訪問rosmontis.com
  1. 首页
  2. 正文

Django安全防御—如何防范跨站脚本攻击(XSS)以及跨站请求伪造(CSRF)攻击

2022年5月6日 2点热度 0人点赞 0条评论

Web开发中十分重要的一项内容就是Web安全,在我们进行服务端构建的时候,最常见的几种Web攻击无非是下面的这几种:

  • 注入(SQL注入)
  • 跨站脚本攻击(XSS)
  • 跨站请求伪造(CSRF)
  • 开放重定向

XSS攻击

XSS(Cross Site Script),即跨站脚本攻击,其原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,而叫做 XSS。

XSS 攻击本质是指攻击者在网站上注入恶意的脚本(如javascript、HTML、Flash),代码未经过滤,浏览器无法区分,恶意脚本被执行,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。

例如,很多博客站,都提供了评论功能:

那么攻击者在表单页面发表评论,输入一些文本之后还输入脚本:

<script>alert('恶意攻击')</script>

当提交评论,当其他用户访问该文章页面的时候都会执行注入的脚本,可以获取用户敏感信息。

如果网站做了转义,脚本就不会被解释执行。

XSS防范

1)输入转义

将内容转义

<div>{{ comment_content }}</div>    <!-- DTL中默认已经开启了自动转义 -->
<div>{{ comment_content | safe }}</div>  <!-- 关闭变量的自动转义,不转义,直接解释执行 不安全 -->

2)输出转义HTML

一般来说,除富文本的输出外,在变量输出到HTML页面时,可以使用编码或转义的方式来防御XSS攻击,

CSRF攻击

CSRF介绍

CSRF(Cross Site Request Forgery),即跨站请求伪造,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,具有很大的危害性。具体来讲,可以这样理解CSRF攻击:攻击者盗用了你的身份,以你的名义向第三方网站发送恶意请求,可以利用你的身份发邮件、发送短信、进行转账,甚至盗取账号。

尽管听起来跟XSS跨站脚本攻击有点相似,但事实上CSRF与XSS差别很大,XSS利用的是站点内的信任用户,而CSRF则是通过伪装来自受信任用户的请求来利用受信任的网站。

CSRF攻击原理

1)首先用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

2)登录信息验证通过以后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

3)用户在没有退出(清除站点A的cookie)网站A的情况下,在同一浏览器中,打开一个TAB页访问网站B;

4)网站B某个页面发出一个请求要求访问网站A;

5)浏览器在接收到这些请求后,在用户不知情的情况下携带浏览器所保存的网站A的cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,网站A根据请求所带的cookie,判断此请求为用户C所发送,因此,站点A会报据用户C的权限来处理恶意站点B所发起的请求,而这个请求可能以用户C的身份发送 邮件、短信、消息,以及进行转账支付等操作,这样恶意站点B就达到了伪造用户C请求站点 A的目的。

受害者只需要做下面两件事情,攻击者就能够完成CSRF攻击:

  • 登录受信任站点 A,并在本地生成cookie;
  • 在不登出站点A(清除站点A的cookie)的情况下,访问恶意站点B。

看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

1)你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

2)你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)

很多情况下所谓的恶意站点,很有可能是一个存在其他漏洞(如XSS)的受信任且被很多人访问的站点,这样,普通用户可能在不知不觉中便成为了受害者。

上面大概地讲了一下CSRF攻击的原理,下面将用几个例子详细说说具体的CSRF攻击,这里以一个银行转账的操作作为例子(仅仅是例子,真实的银行网站没这么傻:>)

例1:

假设银行网站A,以GET请求来发起转账操作,如:http:www.testbank.com/transfer?toBankId=666&money=10000。

在某大型论坛B上,攻击者上传了一张图片,而图片的地址栏中填的并不是图片的地址,而是上面转账地址:

 <img src="http:www.testbank.com/transfer?toBankId=666&money=10000">

当你登录网站A后,没有及时登出,这时又访问了论坛B这个评论的页面,不幸的事情发生了,你会发现你的账号里面10000块不翼而飞了。。。

这到底发生了啥??

事情的真相是,当你登录银行网站A时,浏览器会生成银行A的cookie信息,而当访问论坛B时,页面上的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源http:www.testbank.com/transfer?toBankId=666&money=10000,结果银行网站服务器收到请求后,认为这是你发起的一次转账操作,所以就立刻进行转账操作......

当然,绝大多数网站都不会使用GET请求更新资源,因此,为了杜绝上面的问题,银行将其转账方式改成POST提交。攻击者可能利用论坛B存在的XSS漏洞,评论发表如下代码:

<form id="form" action="http://www.testbank.com/transfer" metdod="POST" style="display:none">
    <input type="text" name="toBankId" value="666"/>
    <input type="text" name="money" value="10000"/>
</form>
<script>
 document.getElementById('form').submit();
</script>

同样当你此时正好登录了银行网站A,没有登出,当你打开这个页面后,脚本会将表单aaa提交,把toBankId和money参数传递给银行的转账地址http:www.testbank.com/transfer,同样的,银行以为是你发起的一次转账会从你账户扣除10000元。

即使论坛没有XSS漏洞,但是攻击者可能使用一个链接诱导你点击,构造页面:

<body>
最新撸羊毛攻略~~
  <form id="form" action="http://www.testbank.com/transfer" metdod="POST"  style="display:none">
   <input type="hidden" name="toBankId" value="666"/>
   <input type="hidden" name="money" value="10000"/>
  </form>

 <script>
  document.getElementById('form').submit();
  
 </script>
</body>

当用户点开这个页面的链接后,会自动发送 POST 请求,然后跳转到原始首页。这样用户在不知情的情况下又没了10000元。

当然,以上只是举例,正常来说银行的交易付款会有USB key、验证码、登录密码和支付密码等一系列屏障,流程比上述流程复杂得多,因此安全系数也高得多。

CSRF防范

1、尽量使用POST,限制GET

GET接口太容易被拿来做CSRF攻击,看上面示例就知道,只要构造一个img标签,而img标签又是不能过滤的数据。接口最好限制为POST使用,GET则无效,降低攻击风险。GET只是仅仅查询数据而已。当然POST并不是万无一失,攻击者只要构造一个form就可以,但需要在第三方页面做,这样就增加暴露的可能性。

2、将cookie设置为HttpOnly

CRSF攻击很大程度上是利用了浏览器的cookie,为了防止站内的XSS漏洞盗取cookie,需要在cookie中设置“HttpOnly”属性,这样通过程序(如JavaScript脚本、Applet等)就无法读取到cookie信息,避免了攻击者伪造cookie的情况出现。

3、增加token

CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于cookie中,因此攻击者可以在不知道用户验证信息的情况下直接利用用户的cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信总不存在于cookie之中。鉴于此,系统开发人员可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务端进行token校验,如果请求中没有token或者token内容不正确,则认为是CSRF攻击而拒绝该请求。假设请求通过POST方式提交,则可以在相应的表单中增加一个隐藏域:

<input type="hidden" name="_toicen" value="tokenvalue"/>

token的值通过服务端生成,表单提交后token的值通过POST请求与参数一同带到服务端,每次会话可以使用相同的token,会话过期,则token失效,攻击者因无法获取到token,也就无法伪造请求。

4、通过Referer识别

根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限的页面的请求都来自于同一个网站。比如某银行的转账是通过用户访问http://www.testbank.com/transfer页面完成的,用户必须先登录www.testbank.com,然后通过单击页面上的提交按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是 提交按钮所在页面的URL(www.testbank.com)。如果攻击者要对银行网站实施CSRF攻击,他只能在其他网站构造请求,当用户通过其他网站发送请求到银行时,该请求的Referer的值是其他网站的地址,而不是银行转账页面的地址。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值即可,如果是以www.testbank.com域名开头的地址,则说明该请求是来自银行网站自己的请求,是合法的,如果Referer是其他网站,就有可能是CSRF攻击,则拒绝该请求。

但是这种方法没什么用,例如爬虫中直接可以在请求头中构造这个值。

现在一般的CSRF防御也都在服务端进行。

1)服务端在收到路由请求时,生成一个随机数,在渲染请求页面时把随机数放到页面里(一般放form表单内)。

2)服务端设置setCookie,把该随机数作为cookie或者session种入用户浏览器。

3)当用户发送 GET 或者 POST 请求时带上_csrf_token参数(对于 Form表单直接提交即可,因为会自动把当前表单内所有的input提交给后台,包括_csrf_token)

4)后台在接受到请求后解析请求的cookie获取_csrf_token的值,然后和用户请求提交的_csrf_token做个比较,如果相等表示请求是合法的。

Django中CSRF处理

Django中处理csrf四种方法:

django 第一次响应来自某个客户端的请求时(get方式),会在服务器端随机生成一个 token,然后把这个 token 写在用户请求的 cookie 里,同时也会给客户端页面发送一个随机的 token (form表单中以{% csrf_token %}方式获取)用以认证。之后客户端每次以 POST 方式向服务端提交请求时,都会带上这个 token,这样就能避免被 CSRF 攻击。

1.在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token;  2.在所有的 POST 表单中,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成,见下面)   3.在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.   4.在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值。

1)首先,在settings.py中开启CSRF中间件。

MIDDLEWARE = [

    'django.middleware.csrf.CsrfViewMiddleware',

]

'''
    django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
    而对于django中设置防跨站请求伪造功能有分为全局和局部。
    全局:

    中间件 django.middleware.csrf.CsrfViewMiddleware

    局部:
    from django.views.decorators.csrf import csrf_exempt,csrf_protect

    @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

    @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
'''

2)对于form表单,我们先看下例子:

<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
    <style>
        #loginbox{
            width: 350px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="loginbox">
        <h2>登录界面</h2>
        <form action="" method="post">
            帐&nbsp;号:<input type="text" name="username">
            <br/>
            密&nbsp;码:<input type="password" name="password">
            <br/>
            <br/>
            <input type="submit" value="登录" id="login_submit">&nbsp;&nbsp;
            <span id="error"></span>
        </form>
    </div>
</body>
</html>

点击登录之后,Django会因为无法通过csrf验证返回一个403:

而CSRF验证其实是对http请求中一段随机字符串的验证,那么这段随机字符串从何而来呢?这个时候我们尝试把login.html做一个修改添加一句 {% csrf_token %}:

<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
    <style>
        #loginbox{
            width: 350px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="loginbox">
        <h2>登录界面</h2>
        <form action="" method="post">
            {% csrf_token %}
            帐&nbsp;号:<input type="text" name="username">
            <br/>
            密&nbsp;码:<input type="password" name="password">
            <br/>
            <br/>
            <input type="submit" value="登录" id="login_submit">&nbsp;&nbsp;
            <span id="error"></span>
        </form>
    </div>
</body>
</html>

我们再通过浏览器元素审查,就会发现一段新的代码:

其实这个 {% csrf_token %}是一个模板标签,我们可以查看源代码实现逻辑:

# {% csrf_token %} 标签的定义:
@register.tag
def csrf_token(parser, token):
    return CsrfTokenNode()


class CsrfTokenNode(Node):
    def render(self, context):
        csrf_token = context.get('csrf_token')  # 服务端获取的csrf_token值
        if csrf_token:
            if csrf_token == 'NOTPROVIDED':
                return format_html("")
            else:
                return format_html('<input type="hidden" name="csrfmiddlewaretoken" value="{}">', csrf_token)  # 生成隐藏域,就是我们在查看页面元素查看到的。
        else:
            ....
           
            return ''

也就是第一次请求页面时,服务器会根据{% csrf_token %}生成一个隐藏域,值是一个随机数,

同时服务器会为响应设置cookie信息,响应头中有set_cookie信息。

当填写完,点击提交表单,隐藏域csrfmiddlewaretoken的值也会一起提交给服务端,并且会携带第一次请求的cookie信息:

然后在CSRF中间件进行处理:

# 通过csrf中间件,找到源码
from django.middleware.csrf import CsrfViewMiddleware


class CsrfViewMiddleware(MiddlewareMixin):
    ....
    
 

用来匹配客户端和服务端cookie是否匹配。

这就是csrf的基本原理,如果没有这样一段随机字符串做验证,我们只要在另一个站点,写一个表单,提交到这个地址下,是一样可以发送数据的,这样就造成了极大的安全隐患。而我们新添加的csrf_token就是在我们自己的站点中,设置的隐藏参数,用来进行csrf验证。

Ajax请求设置csrf_token

3)上面通过form表单形式提交,csrf中间件要验证的随机字符串存放在了form表单中,发送到了后台。如果我们通过ajax请求的方式,怎么传送这样的字符串呢?

方式一

通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。

这种方式比较常用,就是在我们的模板文件中先写一个标签{% csrf_token %}浏览器会自动给我们在页面中生成一个隐藏的input标签,将它的值获取到后,在ajax操作时放到data里传给后台就可以了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
    <style>
        #loginbox{
            width: 350px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="loginbox">
        <h2>登录界面</h2>
        <form action="" method="post">
            {% csrf_token %}
            帐&nbsp;号:<input type="text" name="username">
            <br/>
            密&nbsp;码:<input type="password" name="password">
            <br/>
            <br/>
            <input type="submit" value="登录" id="login_submit">&nbsp;&nbsp;
            <span id="error"></span>
        </form>
    </div>

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    $("#login_submit").click(function (e) {
        // 取消默认的事件,否则submit默认点击也会提交一次请求
        e.preventDefault();
        $.ajax({
            url:"/user/login/",  //请求url
            type:"post",    // 请求方式
            // 请求内容的数据类型(前端发给后端格式)
            //contentType: "application/json; charset=utf-8",
            // 发送JSON数据,需要序列化为JSON字符串
            /*data: JSON.stringify({
                "username": $("input[name='username']").val(),
                "password": $("input[name='password']").val(),
                // 注意csrf随机字符串,后台有固定的名字接收,这种方式是通过标签获取的对应值  
                "csrfmiddlewaretoken":  $("input[name='csrfmiddlewaretoken']").val()
            }),
            */
            data: {
                "username": $("input[name='username']").val(),
                "password": $("input[name='password']").val(),
                // 注意csrf随机字符串,后台有固定的名字接收,这种方式是通过标签获取的对应值
                "csrfmiddlewaretoken": $("input[name='csrfmiddlewaretoken']").val()
            },
            success: function (res) {
                console.log(typeof (res), res);
            }
        });
    });
</script>
</body>

</html>
/* 注意如果前端发送json数据则不行 */

# views.py
class LoginView(View):
    def get(self, request):
            return render(request, 'login.html')

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('password')

        res = {}

        if username and password:  # 此处为了方便测试,没有去数据库进行校验,实际的需要去数据库校验用户名和密码之类的
            res['msg'] = '登录成功'
            res['user'] = username
        else:
            res['msg'] = '用户名或密码错误'

        print(type(res), res)  # <class 'dict'> {'msg': '登录成功', 'user': 'admin'}

        return JsonResponse(res, json_dumps_params={"ensure_ascii": False})
方式二

利用jQuery.ajaxSetup()函数进行AJAX的全局默认设置。

该函数用于更改jQuery中AJAX请求的默认设置选项。之后执行的所有AJAX请求,如果对应的选项参数没有设置,将使用更改后的默认设置。

该函数属于全局jQuery对象。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
    <style>
        #loginbox{
            width: 350px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="loginbox">
        <h2>登录界面</h2>
        <form action="" method="post">
             {# 注意这里form表单里没有加csrftoken标签 #}
            帐&nbsp;号:<input type="text" name="username">
            <br/>
            密&nbsp;码:<input type="password" name="password">
            <br/>
            <br/>
            <input type="submit" value="登录" id="login_submit">&nbsp;&nbsp;
            <span id="error"></span>
        </form>
    </div>

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    $("#login_submit").click(function (e) {
        // 取消默认的事件,否则submit默认点击也会提交一次请求
        e.preventDefault();

        {# 更改jQuery中AJAX请求的默认设置选项 #}
        {# 这样设置也可以通过csrf认证 #}
        $.ajaxSetup({
          data: {
              csrfmiddlewaretoken: '{{ csrf_token }}'

        },
        });


        $.ajax({
            url:"/user/login/",  //请求url
            type:"post",    // 请求方式
            data: {
                "username": $("input[name='username']").val(),
                "password": $("input[name='password']").val(),
            },
            success: function (res) {
                console.log(typeof (res), res);
            }
        });
    });
</script>
</body>
</html>         

在GET请求重新渲染页面的时候,我们可以看到已经生成了一个用于csrfmiddlewaretoken认证的随机字符串了,而且每次刷新页面后的值都是不一样的。

方式三

通过获取返回的cookie中的字符串放置在请求头中发送。其实在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,如果我们发送的是json类型数据的时候,那么csrf_token就不能直接写在data里面了,没有效果,必须通过以下方式来写。

注意:需要引入一个jquery.cookie.js插件。可以去github上搜索这个插件。

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
    <style>
        #loginbox{
            width: 350px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="loginbox">
        <h2>登录界面</h2>
        <form action="" method="post">
            {# 需要写上 #}
            {% csrf_token %}
            帐&nbsp;号:<input type="text" name="username">
            <br/>
            密&nbsp;码:<input type="password" name="password">
            <br/>
            <br/>
            <input type="submit" value="登录" id="login_submit">&nbsp;&nbsp;
            <span id="error"></span>
        </form>
    </div>
    
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
{# 注意引入jquery.cookie.js 进行csrfmiddlewaretoken认证 #}
<script src="{% static 'js/jquery.cookie.js' %}"></script>
<script>
    $("#login_submit").click(function (e) {
        // 取消默认的事件,否则submit默认点击也会提交一次请求
        e.preventDefault();

        $.ajax({
            url:"/user/login/",  //请求url
            type:"post",    // 请求方式
            data: {
                "username": $("input[name='username']").val(),
                "password": $("input[name='password']").val(),
            },
            // 从Cookie取csrftoken,并设置到请求头中,进行csrfmiddlewaretoken认证
            headers: {"X-CSRFToken": $.cookie('csrftoken')},
            success: function (res) {
                console.log(typeof (res), res);
            }
        });
    });
    
    </script>
</body>

</html>

或者可以直接写一个getCookie方法:

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

    $("#login_submit").click(function (e) {
        // 取消默认的事件,否则submit默认点击也会提交一次请求
        e.preventDefault();

        var csrftoken = getCookie('csrftoken');
        console.log(csrftoken);

        $.ajax({
            url:"/user/login/",  //请求url
            type:"post",    // 请求方式
            contentType: "application/json; charset=utf-8",
            data: JSON.stringify({
                "username": $("input[name='username']").val(),
                "password": $("input[name='password']").val(),
            }),
            headers: {"X-CSRFToken": csrftoken},
            success: function (res) {
                console.log(typeof (res), res);
            }
        });
    });
</script>

如果每个ajax请求,都需要这样写太麻烦了,可以使用我们前面提到的$.ajaxSetup()方法,它可以为所有的ajax请求做一个统一配置:

<script>
    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", $.cookie('csrftoken'));              
                xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
                //或者不写{% csrf_token %}标签,这样写也是可以的:
                //xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");

            }
        }
    });

    $("#login_submit").click(function (e) {
        // 取消默认的事件,否则submit默认点击也会提交一次请求
        e.preventDefault();

        $.ajax({
            url:"/user/login/",  //请求url
            type:"post",    // 请求方式
            data: {
                "username": $("input[name='username']").val(),
                "password": $("input[name='password']").val(),
            },
            // 从Cookie取csrftoken,并设置到请求头中,进行csrfmiddlewaretoken认证
            {#headers: {"X-CSRFToken": $.cookie('csrftoken')},#}
            success: function (res) {
                console.log(typeof (res), res);
            }
        });
    });
</script>
</body>

</html>

这种设置请求的方式,主要用在ajax上传文件的时候,上面那个利用ajax上传文件为例子就用到了这种认证的方法!

注意:

如果使用从cookie中取csrftoken的方式,需要确保cookie存在csrftoken值。

如果你的视图渲染的HTML文件中没有包含{% csrf_token %},Django可能不会设置CSRFtoken的cookie。

这个时候需要使用ensure_csrf_cookie()装饰器强制设置Cookie。

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def login(request):
    pass

我们上面是在settings.py中设置了全局中间件,但是,有时候并不一定所有的接口验证都需要进行csrf验证,那怎么办呢?

from django.views.decorators.csrf import csrf_exempt,csrf_protect
 
@csrf_protect
def index(request):
    .....
 
@csrf_exempt
def login(request):
    .....

在CBV中应用

CBV中添加装饰器,需要使用一个装饰器

from django.utils.decorators import method_decorator  #必须使用这个方法
                - 语法:@method_decorator(装饰器函数名称或方法,name='被装饰的函数名')

            #自定义的装饰器
            def wrapper(func):
                def inner(*args,**kwargs):
                    return func(*args,**kwargs)
                return inner
             
            # 1. 指定方法上添加装饰器
                    class Foo(View):
                        @method_decorator(wrapper)    #先导入方法,然后装饰器以参数的形式添加
                        def get(self,request):
                            pass
                                 
                        def post(self,request):
                            pass
            # 2. 在类上添加
                    @method_decorator(wrapper,name='dispatch')
                    class Foo(View):
                        def get(self,request):
                            pass
                        def post(self,request):
                            pass

如果给dispatch添加上就相当于给所有的方法添加了。因为一个请求来了,都是先执行类View的内置函数dispatch,然后再映射到对应的方法上!

标签: 暂无
最后更新:2022年6月8日

Rosmontis

这个人很懒,什么都没留下

点赞

文章评论

取消回复

Rosmontis

这个人很懒,什么都没留下

本站文章约1个月与rosmontis.com同步一次

最新 热点 随机
最新 热点 随机
【拉环社】【Onedrive】仰望夜空的星辰Fine Days/抬头看看吧,看那天上的繁星Fine Days(FD)完整汉化+全CG(4.9GB) 【拉环社】【Onedrive】仰望夜空的星辰IF/抬头看看吧,看那天上的繁星IF -Interstellar Focus-(FD)完整汉化+全CG(2.8GB) 【漩涡社】鲸神的提亚斯提拉/鲸神的Tearstilla 鯨神のティアスティラ 完整汉化+全CG(3.2GB) 【拉环社】【Onedrive】仰望夜空的星辰/抬头看看吧,看那天上的繁星(本作) 见上げてごらん、夜空の星を 完整汉化+全CG(6.5GB) 【SAGA PLANETS】【Onedrive】FD:金色loveriche-金色时光- 金色ラブリッチェ -Golden Time- 完整汉化+全CG(5.5GB) 【SAGA PLANETS】【Onedrive】金辉恋曲四重奏/金色Loveriche 金色ラブリッチェ 完整汉化+全CG(5.6GB) 【Onedrive】娇蛮任性HIGHSPEC ワガママハイスペック 完整汉化(7.2GB) 【Alcot】【Onedrive】FD:将军大人芳华正茂 Fandisc 将軍様はお年頃 ふぁんでぃすく -御三家だヨ!全員集合- 完整汉化+全CG(3.0GB) 【Alcot】【Onedrive】将军大人芳华正茂/将军大人风华正茂 将军様はお年顷 完整汉化+全CG(4.20GB) 国产动画《京剧猫》,被运营耽搁的好动画,哀其不幸怒其不争! 【Onedrive】架向星空之桥AA 架向星空之桥FAN DISC 星空へ架かる桥AA 完整汉化(3.6GB) 【Onedrive】架向星空之桥 星空へ架かる桥 完整汉化(4.3GB) 【颜艺社】【Onedrive】寄宿之恋 かりぐらし恋爱 完整汉化+全CG(2.7GB) 【八月社】【Onedrive】秽翼的尤斯蒂娅 秽翼のユースティア 完整汉化(4.7GB) 【橘子社妈妈累】【Onedrive】回家之前的棉花糖 お家に帰るまでがましまろです 完整汉化+全CG(5.8GB) 【SAGA PLANETS】【Onedrive】花之天使的夏日恋歌 フローラル・フローラブ 完整汉化+全CG(3.7GB) 【音符社】【Onedrive】花与乙女的祝福+花与乙女的祝福 皇家花束 完整汉化(共3.9GB) 【2022.06更新】【Onedrive】最全!花吻在上/亲吻那片花瓣 その花びらにくちづけを 1~20本作+3番外共23作 完整汉化(约10.5GB) 【Onedrive】花色温泉乡/花色七芒星 花色ヘプタグラム 完整汉化(3.6GB) 【CRYSTALIA】红月摇曳的恋之星火SS 与旭同往~来自盛夏的某日~ 旭とワンルーム ~とある夏の一日~ 完整汉化+全CG (550MB) 【CRYSTALIA】红月摇曳的恋之星火SS 与红叶同住 ~来自盛夏的某日~ 紅葉とワンルーム ~とある夏の一日~ 完整汉化+全CG (580MB) 【CRYSTALIA】红月摇曳的恋之星火 紅月ゆれる恋あかり 完整汉化+全CG (2.5GB) 【Onedrive】FD:景之海的艾佩莉亚 ~卡萨布兰卡的骑士~ 景の海のアペイリア ~カサブランカの騎士~ 完整汉化+全CG(2.3GB) 【Onedrive】景之海的艾佩莉娅/海景的艾佩利雅 景の海のアペイリア 完整汉化+全CG(3.0GB) 【柚子社】【Onedrive】管乐恋曲!~The bonds of melody~ ぶらばん! ~The bonds of melody~ 完整汉化(3.0GB) 【Onedrive】【妹抱FD】哥哥,早上起床之前都要抱紧我哦!晚上睡觉之前学更多Java吧!完整汉化+全CG(4.4GB) 【Onedrive】哥哥,早上起床之前都要抱紧我哦!(妹抱)お兄ちゃん、朝までずっとギュってして!完整汉化+全CG(7.0GB) 【SLG】夏日狂想曲:乡间的难忘回忆 【拉环社】【Onedrive】在这苍穹展翅/在这苍穹之中展开双翼 この大空に、翼をひろげてIf My Heart Had Wings 完整汉化(4.3GB) 【拉环社】【Onedrive】在这苍穹展翅-飞行日志- If My Heart Had Wings -Flight Diary- この大空に、翼をひろげて FLIGHT DIARY 完整汉化(3.0GB)
【SLG】夏日狂想曲:乡间的难忘回忆【海豹社】【Onedrive】爱之钥系列 爱之钥田园夏日 アイカギ~アフターデイズ~ 完整汉化+全CG(1.4GB)【拉环社】【Onedrive】仰望夜空的星辰Fine Days/抬头看看吧,看那天上的繁星Fine Days(FD)完整汉化+全CG(4.9GB)【CRYSTALIA】红月摇曳的恋之星火 紅月ゆれる恋あかり 完整汉化+全CG (2.5GB)【柚子社】【Onedrive】管乐恋曲!~The bonds of melody~ ぶらばん! ~The bonds of melody~ 完整汉化(3.0GB)【Onedrive】哥哥,早上起床之前都要抱紧我哦!(妹抱)お兄ちゃん、朝までずっとギュってして!完整汉化+全CG(7.0GB)【Onedrive】景之海的艾佩莉娅/海景的艾佩利雅 景の海のアペイリア 完整汉化+全CG(3.0GB)【Onedrive】FD:景之海的艾佩莉亚 ~卡萨布兰卡的骑士~ 景の海のアペイリア ~カサブランカの騎士~ 完整汉化+全CG(2.3GB)【CRYSTALIA】红月摇曳的恋之星火SS 与红叶同住 ~来自盛夏的某日~ 紅葉とワンルーム ~とある夏の一日~ 完整汉化+全CG (580MB)【CRYSTALIA】红月摇曳的恋之星火SS 与旭同往~来自盛夏的某日~ 旭とワンルーム ~とある夏の一日~ 完整汉化+全CG (550MB)【Onedrive】花色温泉乡/花色七芒星 花色ヘプタグラム 完整汉化(3.6GB)【2022.06更新】【Onedrive】最全!花吻在上/亲吻那片花瓣 その花びらにくちづけを 1~20本作+3番外共23作 完整汉化(约10.5GB)【音符社】【Onedrive】花与乙女的祝福+花与乙女的祝福 皇家花束 完整汉化(共3.9GB)【SAGA PLANETS】【Onedrive】花之天使的夏日恋歌 フローラル・フローラブ 完整汉化+全CG(3.7GB)【橘子社妈妈累】【Onedrive】回家之前的棉花糖 お家に帰るまでがましまろです 完整汉化+全CG(5.8GB)【八月社】【Onedrive】秽翼的尤斯蒂娅 秽翼のユースティア 完整汉化(4.7GB)【颜艺社】【Onedrive】寄宿之恋 かりぐらし恋爱 完整汉化+全CG(2.7GB)【Onedrive】架向星空之桥 星空へ架かる桥 完整汉化(4.3GB)【Onedrive】架向星空之桥AA 架向星空之桥FAN DISC 星空へ架かる桥AA 完整汉化(3.6GB)国产动画《京剧猫》,被运营耽搁的好动画,哀其不幸怒其不争!【Alcot】【Onedrive】将军大人芳华正茂/将军大人风华正茂 将军様はお年顷 完整汉化+全CG(4.20GB)【Alcot】【Onedrive】FD:将军大人芳华正茂 Fandisc 将軍様はお年頃 ふぁんでぃすく -御三家だヨ!全員集合- 完整汉化+全CG(3.0GB)【Onedrive】娇蛮任性HIGHSPEC ワガママハイスペック 完整汉化(7.2GB)【SAGA PLANETS】【Onedrive】金辉恋曲四重奏/金色Loveriche 金色ラブリッチェ 完整汉化+全CG(5.6GB)【SAGA PLANETS】【Onedrive】FD:金色loveriche-金色时光- 金色ラブリッチェ -Golden Time- 完整汉化+全CG(5.5GB)【拉环社】【Onedrive】仰望夜空的星辰/抬头看看吧,看那天上的繁星(本作) 见上げてごらん、夜空の星を 完整汉化+全CG(6.5GB)【漩涡社】鲸神的提亚斯提拉/鲸神的Tearstilla 鯨神のティアスティラ 完整汉化+全CG(3.2GB)【拉环社】【Onedrive】仰望夜空的星辰IF/抬头看看吧,看那天上的繁星IF -Interstellar Focus-(FD)完整汉化+全CG(2.8GB)【雪碧社Sprite】【Onedrive】苍之彼方的四重奏EXTRA2 DL Edition 蒼の彼方のフォーリズム EXTRA2 生肉+全CG(3.5GB)【Onedrive】爱因斯坦携爱敬上APOLLOCRISIS アインシュタインより愛を込めて APOLLOCRISIS 汉化本体+全CG(2.3GB)
HTML实现移动端固定悬浮半透明搜索框 宝塔面板编译安装部署Zabbix5.4最新版(详细图文)-监控端 【Onedrive】异想魅惑 きまぐれテンプテーション (2.0 GB) 如何使用 CSS 连接数据库 使用Cloudreve搭建个人网盘,支持对接阿里腾讯七牛又拍OSS 【CRYSTALIA】红月摇曳的恋之星火SS 与旭同往~来自盛夏的某日~ 旭とワンルーム ~とある夏の一日~ 完整汉化+全CG (550MB) 【明日方舟解包】【spine】异客 贴图立绘+动态骨架文件 安装Zabbix数据库报错:Unable to determine current Zabbix database version: the table "dbversion" was not found. 日常发椿 VB中实现字体自动循环变色 【音符社】【Onedrive】有少女唱响的爱之恋曲~为你所献上的演绎~ (有少女唱响的爱之恋曲FD)(3.4 GB) 【Onedrive】【妹抱FD】哥哥,早上起床之前都要抱紧我哦!晚上睡觉之前学更多Java吧!完整汉化+全CG(4.4GB) 【Onedrive】吃醋大作战 ヤキモチストリーム 完整汉化+全CG(6.3 GB) 基于Python通过API获取热点新闻(天行数据免费接口) 【生肉】【Onedrive】【CRYSTALiA】 RE:D Cherish! 初回版 パッケージ版 已打更新补丁,完整版+特典【3.3G】 【八月社】【Onedrive】秽翼的尤斯蒂娅 秽翼のユースティア 完整汉化(4.7GB) 【鬼畜病毒评测】JJY病毒 【CRYSTALiA】【Onedrive/百度云】共结情缘的剑舞恋曲-椿恋歌-绊きらめく恋いろは-椿恋歌(4.3G) 重装服务器后SSH无法连接服务器 wget提示 要以不安全的方式连接至 domain.com,使用‘--no-check-certificate’ 解决 HTML实现文本框只读不能修改其中的内容 栞那 Mysql如何改远程端口:Mysql如何远程连接端口并修改 How to ask questions?提问的智慧,别像弱智一样提问 【漩涡社】【Onedrive】NEKO-NIN exHeart 3 猫忍之心3 汉化+全CG(2.1G) 俄罗斯破解资源网站RuTracker使用教程!访问及下载方式详解 快速搞定服务器负载均衡—网站文件与数据库的同步 【ADV/Onedrive】传颂之物:二人的白皇 うたわれるもの 二人の白皇(3.5G) 【颜艺社】【Onedrive】寄宿之恋 かりぐらし恋爱 完整汉化+全CG(2.7GB) 【CRYSTALIA】红月摇曳的恋之星火SS 与红叶同住 ~来自盛夏的某日~ 紅葉とワンルーム ~とある夏の一日~ 完整汉化+全CG (580MB)

COPYRIGHT © 2022 Rosmontis&迷迭香的博客&鹤望兰分部. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang