【问题标题】:Django CSRF Token with Axios带有 Axios 的 Django CSRF 令牌
【发布时间】:2020-11-09 01:11:42
【问题描述】:

情况:

我正在尝试使用 Vue.js 作为我的前端和 Django 作为我的后端来构建一个完整的 SPA。这些系统是完全独立的(不是由后端提供index.html 页面的混合应用程序)。

方法

我在我的 Vue-CLI 生成的项目中创建了一个 services 目录,该目录通过 api.js 文件(以下内容)为我的 REST API 提供一般可访问性:

import axios from "axios";
import Cookies from "js-cookie";

axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.xsrfCookieName = "csrftoken";

const BackEnd = "http://127.0.0.1:8000/"; //local backend from manage.py runserver

export default axios.create({
  baseURL: `${BackEnd}api/`,
  timeout: 5000,
  headers: {
    "Content-Type": "application/json",
    "X-CSRFToken": Cookies.get('csrftoken')
  }
});

我怎么知道有这样的令牌可以得到?我编写了一个 API 端点,它在响应标头中提供令牌(如下所示):

Access-Control-Allow-Origin: *
Content-Length: 77
Content-Type: application/json
Date: Sun, 19 Jul 2020 18:04:06 GMT
Server: WSGIServer/0.2 CPython/3.7.6
Set-Cookie: csrftoken=HdM4y6PPOB44cQ7DKmla7lw5hYHKVzTNG5ZZJ2PqAUWE2C79VBCJbpnTyfEdX3ke; expires=Sun, 18 Jul 2021 18:04:06 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Vary: Cookie, Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

问题

虽然我的 Django REST Framework API 正在为我的 GET 请求提供所有数据,但我似乎无法正确分配 csrftoken 来验证我的 POST 请求。即使在我的 axios 请求中适当设置了 X-CSRFToken 标头,我仍然会从服务器获得典型的 403(未设置 CSRF cookie)响应

请求标头

Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 247
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9vOu1sBaQrXtXseR
DNT: 1
Host: 127.0.0.1:8000
Origin: http://127.0.0.1:8080
Referer: http://127.0.0.1:8080/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36
X-CSRFToken: T2Z7pzxKTAuCvBEIjkgRf8RGEEVLYfOyDYkYIcfkWCfSkPB76wCjMMizZvdTQPKg

更新

好的,现在这只是一种痛苦!我在 A) Set-Cookie 响应标头、B) 我的浏览器 cookie 中 csrftoken 的值和 C) 在 axios POST 请求中有不同的令牌值。谁能帮我弄清楚这里发生了什么?

【问题讨论】:

    标签: django vue.js cookies axios csrf


    【解决方案1】:

    我只是在我的 vue 应用中使用了这个,一切都很顺利。

    axios.defaults.xsrfCookieName = 'csrftoken';
    axios.defaults.xsrfHeaderName = 'X-CSRFToken';
    
    axios({
                        method: 'post',
                        url: 'http://127.0.0.1:8000/api/orders-update',
                        xstfCookieName: 'csrftoken',
                        xsrfHeaderName: 'X-CSRFToken',
                        data: updateIDs,
                        headers: {
                            'X-CSRFToken': 'csrftoken',
                        }
                    }).then(response => console.log(response));
    

    【讨论】:

      【解决方案2】:

      姜戈

      您需要在 django 中使用 djoser 进行身份验证

      赖特

      pip install djangorestframework-simplejwt
      pip install djoser
      

      settings.py 更改

      在您的 INSTALLED_APPS 中添加 djoser

      INSTALLED_APPS=[
          ...,
          'djoser',
          ...
      ]
      

      添加你的中间人

      MIDDLEWERE=[
          ...,
          'django.contrib.auth.middleware.AuthenticationMiddleware',
          ...
      ]
      

      添加

      # DRF settings
      REST_FRAMEWORK = {
          # Default permissions
          'DEFAULT_PERMISSION_CLASSES': [
              'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
          ],
          # Token types
          "DEFAULT_AUTHENTICATION_CLASSES": [
              "rest_framework_simplejwt.authentication.JWTAuthentication",
              "rest_framework.authentication.SessionAuthentication"
          ],
      }
      
      
      DJOSER = {
          'PASSWORD_RESET_CONFIRM_URL': 
          'reset_password/{uid}/{token}',
          'ACTIVATION_URL': 'activation/{uid}/{token}',
          'SEND_ACTIVATION_EMAIL': True,
          'SEND_CONFIRMATION_EMAIL': True,
          'TOKEN_MODEL': None,
          'HIDE_USERS': True,
          'SERIALIZERS': {
          },
          'PERMISSIONS': {
              'activation': ['rest_framework.permissions.AllowAny'],
              'password_reset': ['rest_framework.permissions.AllowAny'],
              'password_reset_confirm': ['rest_framework.permissions.AllowAny'],
              'set_password': ['djoser.permissions.CurrentUserOrAdmin'],
              'username_reset': ['rest_framework.permissions.AllowAny'],
              'username_reset_confirm': ['rest_framework.permissions.AllowAny'],
              'set_username': ['djoser.permissions.CurrentUserOrAdmin'],
              'user_create': ['rest_framework.permissions.AllowAny'],
              'user_delete': ['djoser.permissions.CurrentUserOrAdmin'],
              'user': ['djoser.permissions.CurrentUserOrAdmin'],
              'user_list': ['djoser.permissions.CurrentUserOrAdmin'],
              'token_create': ['rest_framework.permissions.AllowAny'],
              'token_destroy': ['rest_framework.permissions.IsAuthenticated'],
          }
      }
      
      
      # JWT settings
      SIMPLE_JWT = {
          'ACCESS_TOKEN_LIFETIME': timedelta(days=2),
          'REFRESH_TOKEN_LIFETIME': timedelta(days=5),
          'ROTATE_REFRESH_TOKENS': False,
          'BLACKLIST_AFTER_ROTATION': True,
          'UPDATE_LAST_LOGIN': False,
      
          'ALGORITHM': 'HS256',
          'SIGNING_KEY': SECRET_KEY,
          'VERIFYING_KEY': None,
          'AUDIENCE': None,
          'ISSUER': None,
      
          'AUTH_HEADER_TYPES': ('JWT',),
          'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
          'USER_ID_FIELD': 'id',
          'USER_ID_CLAIM': 'user_id',
          'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
      
          'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
          'TOKEN_TYPE_CLAIM': 'token_type',
      
          'JTI_CLAIM': 'jti',
      
          'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
          'SLIDING_TOKEN_LIFETIME': timedelta(days=2),
          'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=5),
      }
      
      

      在你的应用 urls.py 添加 djoser url

      urlpatterns = [
          # DRF router
          path('', include(router.urls)),
          # djoser auth urls
          url(r'^auth/', include('djoser.urls')),
          # djoser auth jwt urls
          url(r'^auth/', include('djoser.urls.jwt')),
          # Login GUI DRF
          path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
      ]
      

      Vue

      基础 API URL 项目/src/api/common.js

      import axios from 'axios'
      
      export const HTTP = axios.create({
          baseURL: 'http://api-url',
      })
      

      基础元素 project/src/api/element.js

      import {HTTP} from './common'
      
      function createHTTP(url) {
          return {
              async post(config) {
                  return HTTP.post(`${url}`, config).then(response => {
                      console.log(response)
                      return response.data
                  })
              },
              async get(element) {
                  return HTTP.get(`${url}${element.id}/`)
              },
              async patch(element) {
                  console.log(element)
                  return HTTP.patch(`${url}${element.id}/`, element).then(response => {
                      console.log(response)
                      return response.data
                  })
              },
              async delete(id) {
                  HTTP.delete(`${url}${id}/`)
                  return id
              },
              async list(queryParams = '') {
                  return HTTP.get(`${url}${queryParams}`).then(response => {
                      return response.data.results
                  })
              }
          }
      }
      
      export const Todos = createHTTP(`/todos/`)
      

      你的商店 project/src/store/index.js

      import Vue from 'vue'
      import Vuex from 'vuex'
      import todos from "@/store/modulse/todos";
      
      Vue.use(Vuex)
      
      export default new Vuex.Store({
          modules: {
              todos,
          }
      })
      

      你的突变类型 project/src/store/mutation-types.js

      export const SET_TODOS ='SET_TODOS'
      export const PATCH_TODO ='PATCH_TODO'
      export const DELETE_TODO ='DELETE_TODO'
      export const CREATE_TODO ='CREATE_TODO'
      
      

      你的模块 project/src/store/modulse/todos.js

      import {
          Todos,
      } from '@/api/elements'
      import {
          SET_TODOS, PATCH_TODO, DELETE_TODO, CREATE_TODO
      } from '../mutation-types'
      
      
      // Getters
      export default {
          state: {
              todos: []
          },
          getters: {
              getTodos(state) {
                  return state.todos
              },
          },
      // Mutations
          mutations: {
              [SET_TODOS](state, todos) {
                  state.todos = todos
              },
              [PATCH_TODO](state, todos) {
                  let id = todos.id
                  state.todos.filter(todos => {
                      return todos.id === id
                  })[0] = todos
              },
              [CREATE_TODO](state, todo) {
                  state.todos = [todo, ...state.todos]
              },
              [DELETE_TODO](state, {id}) {
                  state.todos = state.todos.filter(todo =>{
                      return todo.id !==id
                  })
              },
      
          },
      // Actions
          actions: {
              async setTodos({commit}, queryParams) {
                  await Todos.list(queryParams)
                      .then(todos => {
                          commit(SET_TODOS, todos)
                      }).catch((error) => {
                          console.log(error)
                      })
              },
              async patchTodo({commit}, todoData) {
                  await Todos.patch(todoData)
                      .then(todo => {
                          commit(PATCH_TODO, todo)
                      }).catch((error) => {
                          console.log(error)
                      })
              },
              async deleteTodo({commit}, todo_id) {
                  await Todos.delete(todo_id)
                      .then(resp => {
                          commit(DELETE_TODO, todo_id)
                      }).catch((error) => {
                          console.log(error)
                      })
             },
             async createTodo({commit}, todoData) {
                  await Todos.create(todoData)
                      .then(todo => {
                          commit(CREATE_TODO, todo)
                      }).catch((error) => {
                          console.log(error)
                      })
             },
      }
      
      

      在你的项目/src/main.js中

      import Vue from 'vue'
      import store from './store'
      import App from './App.vue'
      import Axios from 'axios'
      
      Vue.prototype.$http = Axios;
      
      new Vue({
        store,
        render: h => h(App),
      }).$mount('#app')
      
      

      在你的项目/src/App.vue

      import {mapActions, mapGetters} from "vuex";
      
      export default {
        name: 'App',
        components: {},
        data() {
          return {}
        },
        methods: {
          ...mapActions(['setTodos','patchTodo','createTodo','deleteTodo']),
        },
        computed: {
          ...mapGetters(['getTodos']),
        },
        async mounted() {
           await this.setTodos()
        },
      }
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-11
        • 2016-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-11
        相关资源
        最近更新 更多