Kis*_*tam 6 django django-csrf vue.js django-cors-headers csrf-token
一段时间以来,我一直在尝试从 Vue 前端向 Django 后端发送 POST 或 DELETE 请求。
我在 localhost:3000 上运行 Vue.js,在 localhost:8000 上运行 Django。我已经使用django-cors-headers设置了 CORS ,并且能够获取请求。但是,一旦我尝试删除或发布,就会收到此错误:
Forbidden (CSRF cookie not set.)
据我了解,我需要在请求标头中传递 CSRF 令牌,我有:
deleteImage() {
const url = this.serverURL + 'images/delete/' + this.image_data.pk;
const options = {
method: "DELETE",
headers: {'X-CSRFToken': this.CSRFtoken}
};
fetch(url, options)
.then(response => {
console.log(response);
if (response.ok){
// if response is successful, do something
}
})
.catch(error => console.error(error));
}
Run Code Online (Sandbox Code Playgroud)
我从 GET 请求获取,如果我使用Django 文档this.CSRFtoken 中显示的方法,令牌是相同的。
我的姜戈settings.py看起来像这样:
rom pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '***'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'serveImages.apps.ServeimagesConfig',
'django_admin_listfilter_dropdown',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000"
]
CSRF_TRUSTED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000"
]
Run Code Online (Sandbox Code Playgroud)
我知道默认情况下django-cors-headers允许 header X-CSRFToken。
我已经浏览了 StackOverflow 上有关此主题的所有先前问题,但似乎没有任何效果。
更多背景:
views.py
from django.http import JsonResponse
import os
from django.conf import settings
from django.middleware import csrf
from .models import Image
def get_csrf_token(request):
token = csrf.get_token(request)
return token
# return JsonResponse({'CSRFtoken': token})
def index(request, dataset, class_label):
payload = {}
images_folder_url = os.path.join('static', 'images', dataset, class_label.lower())
payload['base_url'] = images_folder_url
data_query = Image.objects.filter(dataset__name=dataset, label__name=class_label).values('pk', 'path', 'isIncluded')
payload['image_data'] = list(data_query)
payload['number_of_images'] = len(payload['image_data'])
payload['CSRFtoken'] = get_csrf_token(request)
return JsonResponse(payload)
def delete_image(request, img_pk):
print(request)
# Just for testing
return JsonResponse({'status': '200'})
Run Code Online (Sandbox Code Playgroud)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('get-token', views.get_csrf_token, name='CSRFtoken'),
path('images/<str:dataset>/<str:class_label>', views.index, name='index'),
path('images/delete/<int:img_pk>', views.delete_image, name='delete_image'),
]
Run Code Online (Sandbox Code Playgroud)
好吧,我以前经历过这场战斗,至少可以说是令人沮丧的。如果我完全诚实的话,那是因为我不理解所有涉及的设置的推动力或相互作用。由于没有时间阅读所有文档,仍然不考虑其中的某些内容。不管怎样,有很多潜在的问题,我将介绍一些您可能在这里遇到的问题。
首先,确保您通过相同的 url 运行 Vue.js 应用程序。例如,如果您在 127.0.0.1:8080 上运行 django,那么您的 Vue 应用程序应该在 127.0.0.1:3000 而不是 localhost 上运行。这可能不是你当前的问题,但它可能会给你带来一个糟糕的时刻。如果您的最终设置是从与后端不同的域为前端提供服务,那么您可能需要调整一些设置。
接下来,启用 CORS 以允许跨站点 http 请求中包含 cookie。这可能不是您眼前的问题,但接下来它会困扰您。
# https://github.com/adamchainz/django-cors-headers#cors_allow_credentials
CORS_ALLOW_CREDENTIALS = True
Run Code Online (Sandbox Code Playgroud)
最后,为了真正解决您当前的问题,首先我会改用 axios 来处理您在前端的请求。
import axios from 'axios'
axios.defaults.xsrfHeaderName = 'x-csrftoken'
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.withCredentials = true
let djangoURL = 'http://127.0.0.1:8000'
// `timeout` specifies the number of milliseconds before the request times out.
// Because we enable Django Debug Toolbar for local development, there is often
// a processing hit. This can also be tremendously bad with unoptimized queries.
let defaultTimeout = 30000
if (process.env.PROD) {
djangoURL = 'https://##.##.#.##:###'
defaultTimeout = 10000
}
axios.defaults.baseURL = djangoURL
axios.defaults.timeout = defaultTimeout
const api = axios.create()
export { api }
Run Code Online (Sandbox Code Playgroud)
defaultTimeout 设置和条件本地与产品评估是完全可选的。拥有它真是太好了。发送您的请求应该类似于:
new Promise((resolve, reject) => {
api.delete('images/delete/' + this.image_data.pk).then(response => {
// console.log(response)
resolve(response)
}, error => {
// console.log(error)
reject(error)
})
})
Run Code Online (Sandbox Code Playgroud)
最后一件事,将 http only cookie 的设置设置为 false
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
CSRF_COOKIE_HTTPONLY = False
Run Code Online (Sandbox Code Playgroud)
有时对我有帮助的一个很好的参考示例是这个博客: https ://briancaffey.github.io/2021/01/01/session-authentication-with-django-django-rest-framework-and-nuxt 和这个博客: https://testdriven.io/blog/django-spa-auth/ 第二个设置了一些不适用的设置,并且它处于反应状态,但视图的设置仍然是一个好的开始。您最终会想要合并身份验证,所以现在就选择;会话或 jwt。我选择了基于会话的身份验证,但有些人在想要通过 0auth 等第三方进行身份验证时使用 jwt。
我用于身份验证的视图示例:
import json
# import logging
from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from django.middleware.csrf import get_token
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_POST
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class SessionView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
@staticmethod
def get(request, format=None):
return JsonResponse({'isAuthenticated': True})
class WhoAmIView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
@staticmethod
def get(request, format=None):
return JsonResponse({'username': request.user.username})
@ensure_csrf_cookie
def get_csrf(request):
response = JsonResponse({'detail': 'CSRF cookie set'})
response['X-CSRFToken'] = get_token(request)
return response
@require_POST
def login_view(request):
data = json.loads(request.body)
username = data.get('username')
password = data.get('password')
if username is None or password is None:
return JsonResponse({'detail': 'Please provide username and password.'}, status=400)
user = authenticate(username=username, password=password)
if user is None:
return JsonResponse({'detail': 'Invalid credentials.'}, status=400)
login(request, user)
return JsonResponse({'detail': 'Successfully logged in.'})
def logout_view(request):
if not request.user.is_authenticated:
return JsonResponse({'detail': 'You\'re not logged in.'}, status=400)
logout(request)
return JsonResponse({'detail': 'Successfully logged out.'})
Run Code Online (Sandbox Code Playgroud)
urls.py
urlpatterns = [
path('csrf/', views.get_csrf, name='api-csrf'),
path('login/', views.login_view, name='api-login'),
path('logout/', views.logout_view, name='api-logout'),
path('session/', views.SessionView.as_view(), name='api-session'), # new
path('whoami/', views.WhoAmIView.as_view(), name='api-whoami'), # new
]
Run Code Online (Sandbox Code Playgroud)
编辑:如果您使用 Cookiecutter,另一个问题是为 api 端点设置的限制器。下面的代码位于 base.py 配置中,如果您没有在此 url 下设置所有端点,它们将收到此类错误。
# django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup
CORS_URLS_REGEX = r"^/api/.*$"
Run Code Online (Sandbox Code Playgroud)
因此,要么禁用此功能,要么将您的身份验证置于此 url 下。
| 归档时间: |
|
| 查看次数: |
7930 次 |
| 最近记录: |