36

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

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

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

  • API からの応答ヘッダーを分析しましたが、csrftoken がありません。
  • 残りの API ブラウザを直接見たところ、問題なく表示されました。
  • Django Rest Framework は認証されたユーザーに対してのみ CSRF を強制するため、最初の 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 = '';
    });
};

}]);

良いヒント/アイデア/例を持っている人はいますか?

4

5 に答える 5

37

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

私は現在、同様のセットアップに取り組んでおり、CSRF 保護と組み合わせて CORS を適切に機能させるために戦っていたので、ここで私自身の学習を共有したいと思いました.

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

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

AngularJS アプリは、CSRF Coo​​kie を設定するように、Django API アプリと同じプロジェクトの Django アプリを通じて提供されます。たとえば、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 Web アプリケーションのドメインを CORS_ORIGIN_WHITELIST に追加します。
CORS_ORIGIN_WHITELIST = [
    ...
    'app.mydomain.com',
    ...
]
  • CORS_ALLOW_CREDENTIALS を True に設定します。これは重要です。これを行わないと、リクエストとともに CSRF Coo​​kie が送信されません。

CORS_ALLOW_CREDENTIALS = 真

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 アプリは、index.html を提供して Cookie を設定するだけで済みます。

このアプリの 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
    }
});

これが役立つことを願っています!!

于 2014-03-22T22:13:44.827 に答える
15

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

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

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

于 2013-07-30T04:03:45.637 に答える
7

だから私はこれに対する私自身の解決策を見つけました。うまくいくようです。

これは私のコードの新しいスニペットです:

バックエンド API LoginView (csrf トークンを強制的に body に追加するデコレータを追加)

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」を追加して、CORS を使用してクライアントから API へのヘッダーを許可する必要があります。そうしないと、投稿は拒否されます。

CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
'accept',
'origin',
'authorization',
'X-CSRFToken'

)

それでおしまい!

于 2013-07-30T07:41:25.900 に答える
2

非常に多くの検索の後、私はこのソリューションに着陸し、その作業はローカルシステムとライブ 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アプリで配置する必要がありました

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

于 2017-02-06T04:29:36.963 に答える