AngularJS + Django Rest Framework + CORS (CSRF Cookie не отображается в клиенте)

Я разрабатываю одностраничное приложение в приложении AngularJS и Django Rest Framework + Django CORS Headers.

Моя проблема в том, что cookie "csrftoken" никогда не появляется в моем браузере, когда я связался с бэкэнд.

Например: я делаю логин, используя сообщение. Я получаю "sessionid" cookie правильно, но "csrftoken" никогда не появляется, и поэтому я не могу делать правильные сообщения от моего клиента, так как мне будет отказано из-за отсутствия токена csrf.

  • Я проанализировал заголовки ответов из API, а csrftoken - не ther.
  • Я посмотрел прямо в браузере остальных браузеров, и там он прекрасно выглядит.
  • Как раз отметить, я могу сделать свой первый POST для входа, поскольку Django Rest Framework только заставляет CSRF для аутентифицированных пользователей. Если я попытаюсь переписать его, произойдет с момента появления "sessionid" -cookie.
  • Я не заинтересован в обходе защиты CSRF, поскольку предлагают некоторые сообщения в stackoverflow.

Некоторые фрагменты кода с передней/внутренней стороны. Это незавершенные фрагменты, поэтому не зацикливайтесь на плохо написанном коде.

API-интерфейс 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 = '';
    });
};

}]);

Любой, у кого есть хорошие советы/идеи/примеры?

Ответ 1

Итак, я нашел свое собственное решение, похоже, отлично работает.

Это новые фрагменты кода:

API-интерфейс 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'

)

Вот оно!

Ответ 2

Однопользовательское веб-приложение AngularJS на субдомене A, разговаривающее с API Jang JSON (REST) ​​в поддомене B с использованием защиты CORS и CSRF

Поскольку я в настоящее время работаю над подобной настройкой и боролся за то, чтобы CORS правильно работал в сочетании с CSRF-защитой, я хотел поделиться своими собственными знаниями здесь.

Настройка - SPA и API находятся в разных поддоменах того же домена:

  • AngularJS (1.2.14) Одностраничное веб-приложение на субдоме app.mydomain.com
  • Приложение Django (1.6.2) реализует API JSON REST на поддомене api.mydomain.com

Приложение AngularJS подается через приложение Django в том же проекте, что и APP API Django, так что он устанавливает CSRF Cookie. См. Также, например, Как запустить несколько веб-сайтов из одного проекта Django

Django API App - для того, чтобы работать с CORS и CSRF, мне нужно было сделать следующее на бэкэнде API.

В settings.py для этого приложения (расширение проекта Django settings.py):

  • Добавить приложение corsheaders и промежуточное ПО и промежуточное ПО CSRF:
INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

MIDDLEWARE_CLASSES = (
    ...
    'django.middleware.csrf.CsrfViewMiddleware',
    ...
    'corsheaders.middleware.CorsMiddleware',
)

Также см. заголовки Django CORS на GitHub

  • Добавьте домен для SPA Webapp в CORS_ORIGIN_WHITELIST
CORS_ORIGIN_WHITELIST = [
    ...
    'app.mydomain.com',
    ...
]
  • Установите CORS_ALLOW_CREDENTIALS в значение True. Это важно, если вы этого не сделаете, cookie CSRF не будет отправлен с запросом

CORS_ALLOW_CREDENTIALS = True

Добавьте декоратор обеспечения_csrf_cookie к вашим представлениям, обрабатывающим запросы API JSON:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def myResource(request):
    ...

Приложение Django для AngularJS - приложение AngularJS подается через приложение Django в том же проекте. Это приложение Django настроено для установки CSRF Cookie. Затем токен CSRF из файла cookie используется для запросов к API (который, таким образом, выполняется как часть одного и того же проекта Django).

Обратите внимание, что почти все файлы, связанные с приложением AngularJS, являются только статическими файлами с точки зрения Django. Приложение Django должно обслуживать index.html, чтобы установить файл cookie.

В settings.py для этого приложения (снова расширение проекта Django settings.py), установите CSRF_COOKIE_DOMAIN таким образом, чтобы субдомены также могли их использовать:

CSRF_COOKIE_DOMAIN = ".mydomain.com"

В views.py мне нужно только отобразить файл AngularJS index.html, снова используя декоратор make_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, это гарантирует, что CSRF Cookie будет использоваться в запросе.

Ниже я покажу, как вы можете отправлять запросы на 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
    }
});

Надеюсь, это поможет!

Ответ 3

Непосредственно из документов https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax

Если ваше представление не отображает шаблон, содержащий csrf_token тег шаблона, Django может не устанавливать куки файл токена CSRF. Это обычно в тех случаях, когда формы динамически добавляются на страницу. к обратитесь к этому случаю, Django предоставляет декоратор, который заставляет настройка файла cookie: secure_csrf_cookie().

Поскольку ваше приложение является одностраничным, вы можете добавить ensure_csrf_cookie() в представление, которое отвечает за загрузку начальной страницы.

Ответ 4

Небольшое обновление этого решения.

Так как 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

Надеюсь, это поможет кому-то!

Ответ 5

После столь большого поиска я приземлился на это решение, и его работа формирует меня в локальной системе, а также на реальном сервере веб-фракций, это мое решение для пользователей 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> 

тогда в angular js app вам просто нужно разместить

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;
   }]);

Он работал у меня на платформе Django Angularjs.

https://gist.github.com/mlynch/be92735ce4c547bd45f6