首页
学习
活动
专区
圈层
工具
发布

Django+Vue项目学习第五篇:vue+django发送post请求,解决csrf认证问题

本篇介绍如何在vue端向django发送post请求,以及django处理post请求的方式

这次要实现的功能是:点击【身份证ID】生成指定数量的身份证号

1. 前端main_page.vue相关代码编写

代码语言:javascript
复制
<script>
import axios from 'axios'
import Qs from 'qs'

export default {
  name: "main_page",
  data() {
    return {
      num1: null,
      num2: null,
      info: null,
    }
  },

  methods: {

    create_data(event) {
      if (event.target.id === "b01") {  //通过event.target.id,获取浏览器监听到的点击事件,并查看点击元素的id,通过比对id值判断触发哪个请求
        axios({
          url: "http://localhost:8000/create_data/phone"  //如果不指定method,默认发送get请求
        }).then(res => {
          this.info = res.data
          console.log(res)

        })
      } else if (event.target.id === "b02") {
        console.log('输入的num1='+this.num1)
        let payload1 = {
          num: this.num1,
        }
        axios({
          method: "post",
          headers: {
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
          },
          data: Qs.stringify(payload1),  //发送post请求,使用data关键字接收请求参数
          url: "http://localhost:8000/create_data/id"
        }).then(res => {
          this.info = res.data
          console.log(res)
        })
      } else if (event.target.id === "b03") {
        let payload = {
          num: this.num2
        }
        console.log(payload)
        axios({
          method: "get",
          params: payload,  //发送get请求,使用params关键字接收请求参数
          url: "http://localhost:8000/create_data/name"
        }).then(res => {
          this.info = res.data
          console.log(res)
        })
      }
    },
  },

}
</script>

1、首先导入的一个包 import Qs from 'qs'

2、axios下代码说明

代码语言:javascript
复制
method,指明了请求方法为 post;
headers,添加请求头,请求头中加了一个 Content-Type,为 application/x-www-form-urlencoded; charset=UTF-8;
data,添加携带的参数,这里对参数做了一个处理,使用 Qs.stringify(payload1)对payload进行了序列化处理(如果是application/x-www-form-urlencoded格式的数据,必须要做这个处理,不然django会获取不到请求body中的参数)

2. 后端代码

代码语言:javascript
复制
def create_id(num):
    """生成身份证"""
    identity_ids = [fake.ssn() for i in range(int(num))]

    return " ".join(identity_ids)

@require_http_methods(['GET', 'POST'])
def id(request):

    num = request.POST.get("num")  # "Content-type","application/x-www-form-urlencoded"
    print(num)
    if num == "" or num is None:
        data1 = create_id(5)
    else:
        data1 = create_id(num)
    return HttpResponse(data1)

post请求,如果请求头Content-type=application/x-www-form-urlencoded,可以使用request.POST.get("num"),获取请求携带的参数

ok,到这里先试一下看看有没有正常得到响应,页面点击按钮后如下

貌似还是跨域的问题, 我们继续在 settings.py中新增如下配置

代码语言:javascript
复制
CORS_ALLOW_CREDENTIALS = True  # 允许携带cookie

然后再次发起请求,得到如下结果,仍然报错了

这次的错误是CSRF验证失败

通过查资料得知,这个是django特意加的一个csrf认证,当发送post请求,向服务器提交数据时都要做这个验证,很蛋疼~~

为了解决这个问题,我在网上冲浪了很久很久,终于找到了2个解决方法

解决django-csrf认证-方法1

最简单的方式就是关闭这个验证,把相关配置注释掉,即可跳过认证,自由的发送post请求(如果是自己学习的话,可以采用这个方式,以防心态炸裂,可以愉快地进行后续的学习~)

打开settinngs.py,找到如下代码,把我标记的那一行注释即可

代码语言:javascript
复制
MIDDLEWARE = [

    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',  # 注释这一行可以去掉csrf验证
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

解决django-csrf认证-方法2

网上的资料繁杂,很多博主给的解决方式都已经失效(不清楚是不是和版本有关),结合多篇博客,经过大量实践,终于找到一个可行的解决方法(虽然成功了,但是本人不理解其中的原理,可能需要扒源码才行)

上面我们给settings.py添加了一行配置:

CORS_ALLOW_CREDENTIALS = True # 允许携带cookie

这个配置不要动,继续保留

1、打开views.py,我们需要写一个方法来生成一个csrftoken

代码语言:javascript
复制
from django.middleware.csrf import get_token

def get_csrf_token(request):
    csrf_token = get_token(request)  # 获取csrf_token的值
return JsonResponse({'token': csrf_token})

给这个试图配置路由(等下前端需要调这个方法获取token)

代码语言:javascript
复制
create_data/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.main, name='main'),
    path('phone', views.phone, name='phone'),
    path('id', views.id,name='id'),
    path('name', views.name,name='name'),
    path('get_csrf_token', views.get_csrf_token,name='get_csrf_token'),
]

2、前端axios的配置修改

打开main_page.vue

首先在script标签下添加一行代码

代码语言:javascript
复制
axios.defaults.withCredentials = true  #允许跨域携带cookie信息,必须加上

在script标签下新增一个函数token(),用来调用后台生成csrftoken的函数get_csrf_token()

代码语言:javascript
复制
methods: {
    token() {
      axios.get("http://localhost:8000/create_data/get_csrf_token").then(res => {
        let token = res.data.token
        console.log("**********"+token);
        sessionStorage.setItem("csrf_token", token) //有些博主说把生成的token放到缓存中,然后发请求时拿这个缓存放到请求头中,我试了下这个其实并没有用,可以注释掉,因为我们不拿这个token

      })

    },

    create_data(event) {
    ......
    ......
    ......
}

修改 create_data(event)函数中关于触发生成电话号码的axios请求

代码语言:javascript
复制
methods: {

    create_data(event) {
      this.token()  //调用create_data()函数时,先调用token()函数,请求后台生成csrftoken
      // console.log("..............."+this.token());
      // console.log(typeof event.target.id)
      console.log('cookie='+document.cookie)  //打印浏览器cookie
      let cookie = document.cookie  //提取cookie
      let csrf_token = cookie.split("=")[1]  //提取cookie中的csrftoken
      console.log('cookie_csrf_token='+cookie.split("=")[1])

      if (event.target.id === "b01") {
        ......
        ......
      } else if (event.target.id === "b02") {
        console.log('输入的num1='+this.num1)
        let payload1 = {
          num: this.num1,
        }
        axios({
          method: "post",
          headers: {
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
            'X-CSRFToken': csrf_token,
          },
          data: Qs.stringify(payload1),  //application/x-www-form-urlencoded,发送post请求,使用data关键字接收请求参数
          url: "http://localhost:8000/create_data/id"
        }).then(res => {
          this.info = res.data
          console.log(res)
        })
      } else if (event.target.id === "b03") {
        ......
        ......
      }
    }
  }

(1)首先在create_data()函数下添加了一行代码,来调用刚才新增的token()函数

代码语言:javascript
复制
this.token()

(2)提取浏览器生成的cookie

代码语言:javascript
复制
let cookie = document.cookie  // 通过document.cookie提取cookie
let csrf_token = cookie.split("=")[1]  //提取cookie中的csrftoken

这个cookie应该是django服务器向客户端发送的,通过它来完成csrf验证,post请求必须拿到cookie中的csrftoken然后跟着请求一起发送才行!

(3)最后在headers中加一行

代码语言:javascript
复制
'X-CSRFToken': csrf_token

这个也必须加上,请求头中必须要有这个参数才能被django识别 然后再来尝试发送这个请求,成功了

看一下请求的详细内容

首先如果把token()函数注释掉,看看发送失败和发送成功请求的区别

可以看到,左图的请求中没有携带Cookie,而我们的csrf token是通过document.cookie来获取,所以左图中的X-CSRFToken为空;

网上有人说,可以把后台生成的csrftoken直接赋给请求头中的 X-CSRFToken,我试了一下并不行,还是会提示403Forbidden;

所以通过csrf认证的真正方式是:django配置好跨域允许携带cookie后,并且axios也配置好允许携带cookie,发送post请求时,Django会自动发给客户端一个cookie

我们需要把这个cookie中的csrftoken拿出来再赋给请求头中的 X-CSRFToken,这样才能通过csrf认证

打开chrom浏览器控制台,切换到Application,找到如下位置

发post请求时,这里会自动多出一个cookie,也就是csrftoken

可以自己试一下,如果把这个cookie删掉,发post请求就会报 403Forbidden

如果按照上述配置好的话,每次触发这个请求时,都会在这里自动生成一个cookie,猜测是Django自动发给客户端的

然后客户端需要携带这个cookie才能提高django的csrf验证

当然,如果不按照上述配置,例如 没有配置 axios.defaults.withCredentials = true 或者 前端没有调用后台生成csrftoken的方法,触发post请求时,django服务器便不会发给客户端这个cookie

网上也有博主说可以在axios请求中添加 withCredentials: true,如下形式

代码语言:javascript
复制
axios({
          withCredentials: true,
          method: "post",
          headers: {
           ......

      }

我也试了一下,发现不好用,还是得在顶部配置:axios.defaults.withCredentials = true

按照上述方法我成功解决了Django的csrf验证问题,不过每个人的环境可能不同, 其他人也有可能会遇到别的坑 唉,这个真是卡了我太久了

下一篇
举报
领券