AngularJS + Django Rest Framework + CORS (файл cookie CSRF не отображается в клиенте)
Я занимаюсь разработкой одностраничного приложения на AngularJS с использованием Django Rest Framework + Django CORS Headers.
Моя проблема заключается в том, что файл cookie csrftoken никогда не появляется в моем браузере, когда я связывался с бэкэндом.
Например: я делаю логин используя пост. Я правильно получаю cookie "sessionid", но "csrftoken" никогда не появляется, и поэтому я не могу делать правильные сообщения от моего клиента, так как мне будет отказано из-за отсутствия токена csrf.
- Я проанализировал заголовки ответа от API, а csrftoken - нет.
- Я посмотрел прямо в браузере остальных API, и он хорошо там выглядит.
- Просто чтобы указать, я могу сделать свой первый POST для входа в систему, так как Django Rest Framework вызывает только CSRF для аутентифицированных пользователей. Если я попытаюсь повторно войти в систему, это потерпит неудачу, так как "sessionid"-cookie это присутствует.
- Я не заинтересован в том, чтобы обойти защиту CSRF, как предполагают некоторые посты по stackru.
Некоторые фрагменты кода от front/backend. Это незаконченные фрагменты, так что не зацикливайтесь на плохо написанном коде.
Backend API LoginView
class LoginView(APIView):
renderer_classes = (JSONPRenderer, JSONRenderer)
def post(self, request, format=None):
serializer = LoginSerializer(data=request.DATA)
if serializer.is_valid():
userAuth = authenticate(username=serializer.data['username'], password=serializer.data['password'])
if userAuth:
if userAuth.is_active:
login(request, userAuth)
loggedInUser = AuthUserProfile.objects.get(pk=1)
serializer = UserProfileSerializer(loggedInUser)
user = [serializer.data, {'isLogged': True}]
else:
user = {'isLogged': False}
return Response(user, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Клиентский контроллер AngularJS
.controller('LoginCtrl', ['$scope', '$http', 'uService', '$rootScope', function(scope, $http, User, rootScope) {
scope.login = function() {
var config = {
method: 'POST',
withCredentials: true,
url: rootScope.apiURL+'/user/login/',
data : scope.loginForm
};
$http(config)
.success(function(data, status, headers, config) {
if (status == 200) {
console.log(data[0]); //Test code
// succefull login
User.isLogged = true;
User.username = data.username;
}
else {
console.log(data); //Test code
User.isLogged = false;
User.username = '';
}
})
.error(function(data, status, headers, config) {
console.log('Testing console error');
User.isLogged = false;
User.username = '';
});
};
}]);
Кто-нибудь с хорошими советами / идеями / примерами?
5 ответов
Так что я нашел свое собственное решение, похоже, отлично работает.
Это новые фрагменты моего кода:
Backend API LoginView (добавлен декоратор, заставляющий токен csrf быть добавленным в тело)
class LoginView(APIView):
renderer_classes = (JSONPRenderer, JSONRenderer)
@method_decorator(ensure_csrf_cookie)
def post(self, request, format=None):
c = {}
c.update(csrf(request))
serializer = LoginSerializer(data=request.DATA)
if serializer.is_valid():
userAuth = authenticate(username=serializer.data['username'], password=serializer.data['password'])
if userAuth:
if userAuth.is_active:
login(request, userAuth)
loggedInUser = AuthUserProfile.objects.get(pk=1)
serializer = UserProfileSerializer(loggedInUser)
user = [serializer.data, {'isLogged': True}]
else:
user = {'isLogged': False}
return Response(user, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Клиентская сторона AngularJS (добавить токен в заголовок запроса)
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
Файл настроек на стороне сервера (специально для django-cors-headers)
Первые 5 добавляются по умолчанию, но вам нужно добавить "X-CSRFToken", чтобы разрешить такой заголовок от клиента к API, использующему CORS, иначе публикация будет отклонена.
CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
'accept',
'origin',
'authorization',
'X-CSRFToken'
)
Это оно!
Одностраничное веб-приложение AngularJS на поддомене A, взаимодействующее с API-интерфейсом Django JSON (REST) на поддомене B с использованием защиты CORS и CSRF
Поскольку в настоящее время я работаю над подобной настройкой и боролся за то, чтобы CORS работал правильно в сочетании с защитой CSRF, я хотел бы поделиться своими собственными знаниями здесь.
Настройка - SPA и API находятся в разных поддоменах одного домена:
- AngularJS (1.2.14) одностраничное веб-приложение на поддомене app.mydomain.com
- Приложение Django (1.6.2) реализует JSON REST API на поддомене api.mydomain.com
Приложение AngularJS обслуживается через приложение Django в том же проекте, что и приложение Django API, так что оно устанавливает файл cookie CSRF. Смотрите, например, также Как запустить несколько веб-сайтов из одного проекта Django
Приложение Django API - для обеспечения защиты CORS и CSRF мне нужно было сделать следующее в бэкэнде API.
В файле settings.py для этого приложения (расширение проекта settings.py в Django):
- Добавьте приложение Corsheaders и промежуточное программное обеспечение, а также промежуточное программное обеспечение CSRF:
INSTALLED_APPS = ( ... 'corsheaders', ... ) MIDDLEWARE_CLASSES = ( ... 'django.middleware.csrf.CsrfViewMiddleware', ... 'corsheaders.middleware.CorsMiddleware', )
Также смотрите заголовки Django CORS на GitHub
- Добавьте домен для веб-приложения SPA в CORS_ORIGIN_WHITELIST
CORS_ORIGIN_WHITELIST = [ ... 'app.mydomain.com', ... ]
- Установите для CORS_ALLOW_CREDENTIALS значение True. Это важно, если вы этого не сделаете, файл cookie CSRF не будет отправлен с запросом
CORS_ALLOW_CREDENTIALS = True
Добавьте в ваши представления декоратор sure_csrf_cookie, обрабатывающий запросы JSON API:
from django.views.decorators.csrf import ensure_csrf_cookie @ensure_csrf_cookie def myResource(request): ...
Приложение Django для AngularJS - приложение AngularJS обслуживается через приложение Django в том же проекте. Это приложение Django настроено для установки файла cookie CSRF. Маркер CSRF из файла cookie затем используется для запросов к API (который, таким образом, выполняется как часть того же проекта Django).
Обратите внимание, что почти все файлы, связанные с приложением AngularJS, являются просто статическими файлами с точки зрения Django. Приложение Django должно только служить index.html, чтобы установить cookie.
В файле settings.py для этого приложения (снова расширение проекта settings.py в Django) установите CSRF_COOKIE_DOMAIN так, чтобы их могли использовать поддомены:
CSRF_COOKIE_DOMAIN = ".mydomain.com"
В views.py мне нужно только визуализировать файл AngularJS index.html, снова используя декоратор sure_csrf_cookie:
from django.shortcuts import render from django.views.decorators.csrf import ensure_csrf_cookie # Create your views here. @ensure_csrf_cookie def index(request): return render(request, 'index.html')
Отправка запросов в API с помощью AngularJS - в конфигурации приложения AngularJS установите следующие значения по умолчанию $httpProvider:
$httpProvider.defaults.xsrfCookieName = 'csrftoken'; $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; $httpProvider.defaults.withCredentials = true;
Опять же, обратите внимание на withCredentials, это гарантирует, что в запросе используется файл cookie CSRF.
Ниже я покажу, как вы можете делать запросы к API с помощью сервиса AngularJS $http и JQuery:
$http.post("http://api.mydomain.com/myresource", { field1 : ..., ... fieldN : ... }, { headers : { "x-csrftoken" : $cookies.csrftoken } });
Также см. Модуль ngCookies.
Использование JQuery (1.11.0):
$.ajax("http://api.mydomain.com/myresource", { type: 'POST', dataType : 'json', beforeSend : function(jqXHR, settings) { jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie()); }, cache : false, contentType : "application/json; charset=UTF-8", data : JSON.stringify({ field1 : ..., ... fieldN : ... }), xhrFields: { withCredentials: true } });
Надеюсь, это поможет!!
Непосредственно из документов https://docs.djangoproject.com/en/1.9/ref/csrf/
Если ваше представление не отображает шаблон, содержащий тег шаблона csrf_token, Django может не установить cookie-токен CSRF. Это часто встречается в тех случаях, когда формы динамически добавляются на страницу. Для решения этой проблемы Django предоставляет декоратор представления, который принудительно устанавливает настройку cookie: sure_csrf_cookie().
Поскольку ваше приложение является одностраничным, вы можете добавить ensure_csrf_cookie()
к представлению, которое отвечает за начальную загрузку страницы.
Небольшое обновление к этому решению.
Начиная с AngularJS 1.2.10 вам необходимо установить файл cookie CSRF для каждого типа запроса в клиенте:
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers.put['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers['delete']['X-CSRFToken'] = $cookies.csrftoken;
Это связано со следующим изменением, произошедшим между 1.2.9 и 1.2.10 https://github.com/cironunes/angular.js/commit/781287473bc2e8ee67078c05b76242124dd43376
Надеюсь, это поможет кому-то!
После столь длительного поиска я нашел это решение и его работу со мной в локальной системе, а также на живом веб-сервере фракций. Это мое решение для пользователей Django. Перейдите в папку apache, расположенную в проекте, а затем в бен, который вы найдете.
httpd.conf или конфигурацию вашего сервера для php или других пользователей (обычно они находятся в файле *.conf, например httpd.conf или apache.conf) или в.htaccess. затем просто добавьте этот код
<IfModule mod_headers.c>
SetEnvIf Origin (.*) AccessControlAllowOrigin=$1
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
тогда в угловом приложении JS вам просто нужно было разместить
angular.module('app', ['ngCookies'])
.config([
'$httpProvider',
'$interpolateProvider',
function($httpProvider, $interpolateProvider, $scope, $http) {
$httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
}]).
run([
'$http',
'$cookies',
function($http, $cookies) {
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
}]);