web-dev-qa-db-ja.com

AngularJS + Django Rest Framework + CORS(CSRF Coo​​kieがクライアントに表示されない)

とDjango Rest Framework + Django CORSヘッダーを使用して、AngularJSで1ページのアプリケーションを開発しています。

私の問題は、バックエンドに接続したときに「csrftoken」Cookieがブラウザーに表示されないことです。

例:投稿を使用してログインしています。 「sessionid」Cookieは正しく取得されますが、「csrftoken」は表示されず、csrfトークンがないため拒否されるため、クライアントから適切な投稿を行うことができません。

  • APIからの応答ヘッダーを分析しましたが、csrftokenはそうではありません。
  • 残りのAPIブラウザーを直接調べたところ、そこに表示されます。
  • ちょうど指摘するために、私は最初のPOST Djangoからログインするために、POST $ ===を行うことができます。 「sessionid」-cookieが存在するため。
  • Stackoverflowに関するいくつかの投稿が示唆しているように、私はCSRF保護を迂回することに興味がありません。

フロント/バックエンドからのコードスニペット。これらは未完成のスニペットであるため、記述が不十分なコードにこだわらないでください。

バックエンド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 = '';
    });
};

}]);

良いヒント/アイデア/例はありますか?

33
JoakimB

CORSおよびCSRF保護を使用してサブドメインBのDjango JSON(REST)APIと通信するサブドメインAのAngularJSシングルページWebアプリケーション

現在、同様の設定に取り組んでおり、CSRF保護と組み合わせてCORSを適切に機能させるために戦っていたので、ここで自分の学習を共有したいと考えました。

Setup-SPAとAPIは両方とも同じドメインの異なるサブドメインにあります。

  • AngularJS(1.2.14)サブドメインapp.mydomain.comの単一ページWebアプリケーション
  • Django App(1.6.2)は、サブドメインapi.mydomain.comでJSON REST APIを実装します

AngularJSアプリは、Django APIアプリと同じプロジェクトのDjangoアプリを通じて提供され、CSRF Coo​​kieを設定します。たとえば、次も参照してください 1つのDjangoプロジェクトから複数のWebサイトを実行する方法

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',
)

GitHubのDjango CORSヘッダー も参照してください。

  • SPA WebappのドメインをCORS_Origin_WHITELISTに追加します
CORS_Origin_WHITELIST = [
    ...
    'app.mydomain.com',
    ...
]
  • CORS_ALLOW_CREDENTIALSをTrueに設定します。これは重要です。これを行わない場合、CSRF Coo​​kieはリクエストとともに送信されません。

CORS_ALLOW_CREDENTIALS = True

JSON APIリクエストを処理するビューにensure_csrf_cookieデコレータを追加します。

from Django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def myResource(request):
    ...

AngularJS用のDjangoアプリ-AngularJSアプリは、同じプロジェクトのDjangoアプリを介して提供されます。このDjangoアプリは、CSRF Coo​​kieを設定するように設定されています。 CookieからのCSRFトークンは、APIへの要求に使用されます(したがって、同じDjangoプロジェクトの一部として実行されます)。

AngularJSアプリケーションに関連するほとんどすべてのファイルは、Djangoの観点から見ると静的ファイルであることに注意してください。 Djangoアプリは、cookieを設定するためにindex.htmlを提供するだけです。

このアプリのsettings.py(Djangoプロジェクトのsettings.pyの拡張子)で、サブドメインでも使用できるようにCSRF_COOKIE_DOMAINを設定します。

CSRF_COOKIE_DOMAIN = ".mydomain.com"

Views.pyでは、AngularJS index.htmlファイルをレンダリングするだけで、再度ensure_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')

AngularJSを使用してAPIにリクエストを送信-AngularJSアプリの設定で、次の$ httpProviderのデフォルトを設定します。

$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.withCredentials = true;

繰り返しますが、withCredentialsに注意してください。これにより、CSRF Coo​​kieがリクエストで使用されます。

以下に、AngularJS $ httpサービスとJQueryを使用してAPIにリクエストを送信する方法を示します。

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

これがお役に立てば幸いです!!

35
Visionscaper

ドキュメントから直接 https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax

ビューがcsrf_tokenテンプレートタグを含むテンプレートをレンダリングしていない場合、DjangoはCSRFトークンCookieを設定しない可能性があります。これは、フォームがページに動的に追加される場合に一般的です。 、Djangoは、Cookieの設定を強制するビューデコレータを提供します:ensure_csrf_cookie()。

アプリケーションは単一ページのアプリケーションであるため、ensure_csrf_cookie()を最初のページの読み込みを担当するビューに追加できます。

15
Derek Kwok

このソリューションの小さな更新。

AngularJS 1.2.10以降では、クライアントのリクエストタイプごとにCSRF Coo​​kieを設定する必要があります。

$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

これが誰かを助けることを願っています!

6
JoakimB

非常に多くの検索の後、私はこのソリューションに着陸し、その作業はローカルシステム上およびライブWebファクションサーバー上で私を形作りますこれはDjangoユーザー向けの私のソリューションプロジェクトにあるApacheフォルダに移動してから、ビンで見つけます

httpd.confまたはphpまたは他のユーザーのサーバー構成(通常はhttpd.confやApache.confなどの* .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

2
Akshay Kumbhar