问题
I am trying to make a POST request from an Angular 6 app to a Django backend. Even though I include the csrf token in the headers, Django is logging a 403 Forbidden error as "CSRF token missing or incorrect". My code is as follows (with extraneous headers in my attempts to satisfy Django):
Angular Component:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { SharedService } from '../../shared.service';
import { CookieService } from 'ngx-cookie-service';
@Injectable({
providedIn: 'root'
})
export class EmailService {
// http options used for making any writing API calls with csrf token
private httpOptions: any;
csrfToken;
constructor(private http: HttpClient, private cookieService: CookieService) {
// Set the csrf token
this.http.get(SharedService.contactEmailUrl).subscribe((data) => (this.csrfToken = data['csrfToken']), (error1) => console.log(error1));
}
sendMailgunContactMessage(payload) {
// Configure CSRF token header options
this.httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': this.csrfToken,
'x-csrftoken': this.csrfToken,
'X-XSRF-TOKEN': this.csrfToken,
'XSRF-TOKEN': this.csrfToken,
'X-CSRF': this.csrfToken,
csrfmiddlewaretoken: this.csrfToken,
csrftoken: this.csrfToken
}),
withCredentials: true
};
let body = {
csrfmiddlewaretoken: this.csrfToken,
content: payload
};
return this.http.post(SharedService.contactEmailUrl, body, this.httpOptions);
}
}
Django Settings:
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'XYZ'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
CSRF_COOKIE_SECURE = False
CSRF_USE_SESSIONS = False
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
'XYZ'
)
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'X-CSRFToken',
'x-csrftoken',
'X-XSRF-TOKEN',
'XSRF-TOKEN',
'csrfmiddlewaretoken',
'csrftoken',
'X-CSRF'
)
CORS_ALLOW_CREDENTIALS = True
# Application definition
INSTALLED_APPS = (
'corsheaders',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
)
MIDDLEWARE_CLASSES = (
'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
MIDDLEWARE = (
'django.middleware.csrf.CsrfViewMiddleware'
)
ROOT_URLCONF = 'django_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'django_project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
# Allow Django from all hosts. This snippet is installed from
# /var/lib/digitalocean/allow_hosts.py
import os
import netifaces
# Find out what the IP addresses are at run time
# This is necessary because otherwise Gunicorn will reject the connections
def ip_addresses():
ip_list = []
for interface in netifaces.interfaces():
addrs = netifaces.ifaddresses(interface)
for x in (netifaces.AF_INET, netifaces.AF_INET6):
if x in addrs:
ip_list.append(addrs[x][0]['addr'])
return ip_list
# Discover our IP address
ALLOWED_HOSTS = ip_addresses()
Django View:
from django.shortcuts import render
from django.http import HttpResponse
from .controllers import *
import json
from django.middleware.csrf import get_token
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.csrf import csrf_protect
# Create your views here.
@ensure_csrf_cookie
def email(request):
if request.method == 'POST':
json_body = json.loads(request.body)
response = HttpResponse(send_contact_message(json_body))
return response
elif request.method == 'GET':
csrf_token = get_token(request)
response = HttpResponse('{ "csrfToken": "' + csrf_token + '" }')
return response
Note: Django is not setting a csrftoken cookie in cookies, not sure if that matters or not.
Why is the CSRF token(s) that I send back in the headers not being verified by Django?
回答1:
try with this in your module instead of csrf headers
@NgModule({
providers: [ HttpXsrfInterceptor,
{ provide: HTTP_INTERCEPTORS, useExisting: HttpXsrfInterceptor, multi: true },
{ provide: HttpXsrfTokenExtractor, useClass: HttpXsrfCookieExtractor },
{ provide: XSRF_COOKIE_NAME, useValue: 'XSRF-TOKEN' },
{ provide: XSRF_HEADER_NAME, useValue: 'X-XSRF-TOKEN' }, ] })
replace the 'xsrf-token' and 'x-xsrf-token' with the value django sends back.
from: https://angular.io/api/common/http/HttpClientXsrfModule
回答2:
Just import HttpClientXsrfModule to your project, it will take care of reading the cookie and resending it as a custom header in every request.
The cookie and header names are not a standard, but rather a convention, so you can configure them if the default ones don't match your backend's ones.
As it happens, Django's cookie name and header name don't match Angular default ones so HttpClientXsrfModule
has to be imported withOptions
like this:
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
@NgModule({
...
imports:[..., HttpClientXsrfModule.withOptions({ cookieName: 'csrftoken', headerName: 'X-CSRFToken' }), ...]
...
})
来源:https://stackoverflow.com/questions/51601219/django-to-angular-6-csrf-token-missing-or-incorrect-even-though-its-set-in-the