vue与django CSRF认证(确切可行方案)

时间:2024-02-26 08:25:52

vue与django交互参考  《django+vue+vue-resource+django-cors-headers实现前后端分离

vue+django      django用的是vue  build后的 dist目录   启动用django共用一个端口,不存在跨域问题

要实现csrf认证首先要了解django  CSRF原理

CSRF跨域是不会自动生成csrftoken的(深坑,之前POST后端报错csrf not set 就是这个原因)

post请求前必须先get一次产生token

手动生成csrftoken

1request.META["CSRF_COOKIE_USED"] = True
2手动调用 csrf 中的 get_token(request) 或 rotate_token(request) 方法
3html表单带{%csrf_token%} (适用于django render渲染的前端)

在vivews.py设置

# Create your views here.
from django.shortcuts import  render,HttpResponse,redirect
from blog.models import *
from django.views.decorators.http import require_http_methods
import json
from django.core import serializers
from django.http import JsonResponse
from django.middleware.csrf import get_token ,rotate_token
import simplejson
@require_http_methods(["GET",\'POST\'])
def add_book(request):
    response = {}
    try:
        if  request.method==\'GET\':
            get_token(request)  #或request.META["CSRF_COOKIE_USED"] = True   新加产生token
        if  request.method==\'POST\':
            req = simplejson.loads(request.body)  # json格式传过来的数
            Book.objects.create(
                **req
            )
            # Book.objects.create(   #x-www-form-urlencoded传过来的数据
            #     **{"book_name":request.POST.get(\'book_name\')},
            # )
            response[\'msg\'] = \'success\'
            response[\'error_num\'] = 0
        # if request.method==\'GET\':
        #     # book = Book(book_name=request.GET.get(\'book_name\'))
        #     # book.save()
        #     Book.objects.create(  # 方法二建议这种,直接将前端字典插入
        #         **{"book_name":request.GET.get(\'book_name\')},
        #     )
        #     response[\'msg\'] = \'success\'
        #     response[\'error_num\'] = 0
    except  Exception as e:
        response[\'msg\'] = str(e)
        response[\'error_num\'] = 1
    return JsonResponse(response)

@require_http_methods(["GET",\'POST\'])
def show_books(request):
    response = {}
    get_token(request)
    try:
        print(request.POST)
        print(request.body)
        books = Book.objects.filter()
        response[\'list\']  = json.loads(serializers.serialize("json", books))
        response[\'msg\'] = \'success\'
        response[\'error_num\'] = 0
    except  Exception as e:
        response[\'msg\'] = str(e)
        response[\'error_num\'] = 1

    return JsonResponse(response)     

 

vue前端设定 main.js入口

 

import Vue from \'vue\'
import App from \'./App\'
import router from \'./router\'
import VueResource from \'vue-resource\'
Vue.use(VueResource);
Vue.config.productionTip = false;
//Vue.http.options.xhr = { withCredentials: true };
//Vue.http.options.emulateJSON = true;
//Vue.http.interceptors.push(function(request, next) {//拦截器
// 跨域携带cookie
// request.credentials = true;
//next()
//});    //vue未build跨域使用的,跨域建议代理。更简便

new Vue({
  el: \'#app\',
  router,
  components: { App },
  template: \'<App/>\'
});

 

 

 

Vue.http.options.xhr = { withCredentials: true }; 的用途就是允许跨域请求携带cookie做身份认证
Vue.http.options.emulateJSON = true; 的作用是如果web服务器无法处理application/json的请求,你可以启用emulateJSON选项。启用该选项后请求会以application/x-www-form-urlencoded作为MIME type,就像普通的HTML表单一样
拦截器主要是作用是可以在请求前和发送请求后做一些处理
因为跨域请求是不会提供任何凭据的(cookie、HTTP认证及客户端SSL证明等),但是我们可以通过设置request.credentials = true;来表示允许

components/submit.vue

<template>
<div id="tomato">
  <form @submit.prevent="submit">
      <div class="field">
          书名:
          <input type="text" v-model="book.book_name">
      </div>

      <input type="submit"
             value="提交">
  </form>
  <button v-on:click="display">搜索</button>
  <table v-for="item in books">
    <tr>
      <td>书名:{{ item.fields.book_name}} && 添加时间:{{item.fields.add_time}}</td>
    </tr>
  </table>
</div>
</template>
<script>
  export default {
    data (){
      return {
        books: [],
        book:{
          book_name:null,
      }
    }
    },
//    mounted() {
//      // GET /someUrl
//      this.$http.get(\'http://127.0.0.1:8000/api/show_books\').then(response => {
//        this.books = response.data.list;
//        console.log(this.books[0].fields.book_name);
//        // get body data
//        // this.someData = response.body;
//
//      }, response => {
//        console.log("error");
//      });
//    },
    methods: {
        submit: function() {   //post函数
          var formData = JSON.stringify(this.book); // 这里才是你的表单数据
          console.log(formData);
          this.$http.get(\'http://127.0.0.1:8000/api/add_book\'); //产生COOKIE 必须先get一次不然会报错csrf not set
          sleep(4000); //等级几秒等,不然可能获取不到cookie
          var DjangoCookie=getCookie(\'csrftoken\');
          //发送过去的headers带上X-CSRFToken进行认证,值就是cookie csrftoken的值
          this.$http.post(\'http://127.0.0.1:8000/api/add_book\',formData,{headers:{\'X-CSRFToken\':DjangoCookie}},{emulateJSON: true}).then((response) => {
              // success callback
              console.log(response.data);
          }, (response) => {
               console.log("error");
              // error callback
          });
//          this.$http({       //此方法可以通过CSRF认证但是数据过不去,未深究
//                    method:\'POST\',
//                    url:\'http://127.0.0.1:8000/api/add_book\',
//                    data:{\'book_name\':\'lxs\'},
//                    headers: {"X-CSRFToken": DjangoCookie},
//                    emulateJSON: true
//                    }).then((response) => {
//              // success callback
//              console.log(response.data);
//          }, (response) => {
//               console.log("error");
//              // error callback
//          });
        },
        display() {
      // GET /someUrl URL为django接口地址
        this.$http.get(\'http://127.0.0.1:8000/api/show_books\').then(response => {
          this.books = response.data.list;
          console.log(this.books[0].fields.book_name);
          // get body data
          // this.someData = response.body;

        }, response => {
          console.log("error");
        });
//          this.$http({   //结果同上get
//          method:\'GET\',
//          url:\'http://127.0.0.1:8000/api/show_books\'}).then(response => {
//          this.books = response.data.list;
//          console.log(this.books[0].fields.book_name);
//          // get body data
//          // this.someData = response.body;
//
//        }, response => {
//          console.log("error");
//        });
    },

    },
  }
function getCookie(name){  //获取cookie函数
    name = name + "=";
    var start = document.cookie.indexOf(name),
        value = null;
    if(start>-1){
        var end = document.cookie.indexOf(";",start);
        if(end == -1){
            end = document.cookie.length;
        }
        value = document.cookie.substring(start+name.length,end);
    }
    return value;
}
function sleep(numberMillis) {   //等待函数
    var now = new Date();
    var exitTime = now.getTime() + numberMillis;
    while (true) {
        now = new Date();
        if (now.getTime() > exitTime)
            return;
    }
}
</script>

 vue-resource 引入headers 除了局部引入,还可以在全局引入(在main,js引入后所有post请求都只需提交数据就行)

import Vue from \'vue\'
import App from \'./App\'
import router from \'./router\'
import VueResource from \'vue-resource\'
import ElementUI from \'element-ui\';
import \'element-ui/lib/theme-chalk/index.css\';
import store from \'./store/store\'
Vue.use(ElementUI);
Vue.use(VueResource);
Vue.config.productionTip = false;
Vue.http.get(\'http://127.0.0.1:8000/api/add_book\').then(response => {});
let djangocookie=getCookie(\'csrftoken\');
Vue.http.headers.common[\'X-CSRFToken\'] = djangocookie;//这里设置请求头
router.beforeEach((to, from, next) => {
if (to.path === \'/login\') {
    next()
  } else {
    if (!store.state.user ) {
      next({ path: \'/login\' })
    } else {
      next()
    }
  }
});
new Vue({
  el: \'#app\',
  router,
  store,
  components: { App },
  template: \'<App/>\'
});

function getCookie(name){  //获取cookie函数
    name = name + "=";
    let start = document.cookie.indexOf(name),
        value = null;
    if(start>-1){
        let end = document.cookie.indexOf(";",start);
        if(end === -1){
            end = document.cookie.length;
        }
        value = document.cookie.substring(start+name.length,end);
    }
    return value;
}

 

OK, 结束,完美收工,差点被百度的坑埋了。。。。相当。。。。。。。。。。。。。