带有 Axios 的 Django CSRF 令牌
Posted
技术标签:
【中文标题】带有 Axios 的 Django CSRF 令牌【英文标题】:Django CSRF Token with Axios 【发布时间】: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: `$BackEndapi/`,
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 请求中有不同的令牌值。谁能帮我弄清楚这里发生了什么?
【问题讨论】:
【参考方案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()
,
【讨论】:
以上是关于带有 Axios 的 Django CSRF 令牌的主要内容,如果未能解决你的问题,请参考以下文章
带有 jwt 身份验证的 django rest api 要求 csrf 令牌
带有 axios 和 vue 的 laravel 5.8 中的 CSRF
如何使用 Axios 将 CSRF Cookie 从 React 发送到 Django Rest Framework
Laravel + Vue.js (axios) - CSRF 令牌不匹配