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

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, 结束,完美收工,差点被百度的坑埋了。。。。相当。。。。。。。。。。。。。

坚持到无能为力,拼搏到感动自己
原文地址:https://www.cnblogs.com/linxizhifeng/p/8995077.html