24

angular + laravel rest アプリケーションを構築しようとしています。データベースのビューを取得できます。新しいアイテムを追加しようとすると。csrf500 errorトークンが一致しないというメッセージが表示されます。私のフォームのレイアウトは次のとおりです。

<form class="form-horizontal" ng-submit="addItem()">

  <input type="text" ng-model="itemEntry" placeholder="Type and hit Enter to add item">
</form>

これは、アイテムをデータベースに追加しようとする方法です:

$scope.addItem = function(CSRF_TOKEN) {
    $http.post('/shop', { text: $scope.itemEntry, csrf_token: CSRF_TOKEN} ).success(function(data, status) {
        if(data) {
            var last = _.last($scope.items);
            _token = CSRF_TOKEN;
            $scope.items.push({text: $scope.itemEntry, bought: false, id: (last.id + 1) });
            $scope.itemEntry = '';
            console.log($scope.items);
        } else {
            console.log('There was a problem. Status: ' + status + '; Data: ' + data);
        }
    }).error(function(data, status) {
            console.log('status: ' + status);
        });

}

アプリケーションに使用するフィルターは次のとおりです。

Route::filter('csrf', function()
{
    if (Session::token() != Input::get('_token'))
    {
        throw new Illuminate\Session\TokenMismatchException;
    }
});

私のブレードビューでは、これを使用して動作します:

<input type="hidden" name="_token" value="{{ csrf_token() }}" />

HTML フォームを使用するときに csrf_token を送信するにはどうすればよいですか?

ありがとう

編集 1: このようにポスト リクエストにヘッダーを追加しても、エラーは発生しません。

  $http({
    method  : 'POST',
    url     : '/shop',
    data    :  $scope.itemEntry,  // pass in data as strings
    headers : { 'Content-Type': 'application/x-www-form-urlencoded' }   
  });
4

5 に答える 5

29

オプションは、CSRF トークンを定数として注入することです。以下を head タグに追加します。

<script>
  angular.module("app").constant("CSRF_TOKEN", '{{ csrf_token() }}');
</script>

その後、必要に応じてモジュールメソッドに注入できます。

app.factory("FooService", function($http, CSRF_TOKEN) {
    console.log(CSRF_TOKEN);
};

このLaravel + AngularJS プロジェクトのサンプルのソース コードを覗いてみたいと思われるかもしれません。

于 2013-08-20T15:09:24.563 に答える
23

Laravel 5 を使用する場合、CSRF トークンを Angular http ヘッダーに追加する必要はありません。

Angular を搭載した Laravel 5 では、これが自動的に行われます。

http://laravel.com/docs/5.1/routing#csrf-x-xsrf-token

于 2015-06-17T12:54:02.353 に答える
23

Rubens Mariuzzo によって受け入れられた解決策は機能しますが、より良いと思われる別の解決策を見つけたと思います。

この方法では、html スクリプトから angularjs アプリにデータを渡す必要がなく、懸念事項がより適切に分離されます。たとえば、これにより、Laravel APP を単なる API として持つことができます。

私の解決策は、API リクエストを介して CSRF トークンを取得し、この値を定数として設定することです。

さらに、必要に応じて CSRF トークンを挿入する代わりに、API http 要求時にサーバーによってチェックされるデフォルト ヘッダーにトークンを設定します。

例はlaravelを示していますが、深刻なフレームワークは同様のものを提供できるはずです。

LARAVEL の CSRF ルート:

// Returns the csrf token for the current visitor's session.
Route::get('api/csrf', function() {
    return Session::token();
});

before => 'api.csrf'フィルターによるルートの保護

// Before making the declared routes available, run them through the api.csrf filter
Route::group(array('prefix' => 'api/v1', 'before' => 'api.csrf'), function() {
Route::resource('test1', 'Api\V1\Test1Controller');
Route::resource('test2', 'Api\V1\Test2Controller');
});

api.csrfフィルター_

// If the session token is not the same as the the request header X-Csrf-Token, then return a 400 error.
Route::filter('api.csrf', function($route, $request)
{
if (Session::token() != $request->header('X-Csrf-Token') )
{
    return Response::json('CSRF does not match', 400);
}
});

AngularJS のものはこれを app.js に入れます:

ブロッキング バージョン:

var xhReq = new XMLHttpRequest();
xhReq.open("GET", "//" + window.location.hostname + "/api/csrf", false);
xhReq.send(null);

app.constant("CSRF_TOKEN", xhReq.responseText);

app.run(['$http', 'CSRF_TOKEN', function($http, CSRF_TOKEN) {    
    $http.defaults.headers.common['X-Csrf-Token'] = CSRF_TOKEN;
}]);

ノンブロッキング版

var xhReq = new XMLHttpRequest();
xhReq.open("GET", "//" + window.location.hostname + "/api/csrf", true);

xhReq.onload = function(e) {
  if (xhReq.readyState === 4) {
    if (xhReq.status === 200) {
      app.constant("CSRF_TOKEN", xhReq.responseText);

      app.run(['$http', 'CSRF_TOKEN', function($http, CSRF_TOKEN) {
        $http.defaults.headers.common['X-Csrf-Token'] = CSRF_TOKEN;
      }]);
    }
  }
};

xhReq.send(null);

CSRF_TOKEN 定数が AngularJS アプリからのすべての http リクエストのヘッダーとして挿入され、すべての API ルートが保護されるようになりました。

于 2014-05-20T12:50:44.980 に答える
7

私のソリューションは痛みが少なく、柔軟性が高いと思います。特に、カルマでアプリをテストすることを考えています。

まず、このコードをマスター ビューに追加します

 <meta name="csrf-token" content="{{ csrf_token() }}">

ルートを追加せずにcsrfトークンをhtmlコンテンツに保存しました。

AngularJs アプリのすべてのリクエストを CSRF トークンで保護するようになりました

/**
 * 
 * when it thinks testing your app unit test with Karma,
 * this solution was better than getting token via AJAX.
 * Because low-level Ajax request correctly doesn't work on Karma
 * 
 * Helper idea to me :
 * http://stackoverflow.com/questions/14734243/rails-csrf-protection-angular-js-protect-from-forgery-makes-me-to-log-out-on/15761835#15761835 
 * 
 */
var csrftoken =  (function() {
    // not need Jquery for doing that
    var metas = window.document.getElementsByTagName('meta');

    // finding one has csrf token 
    for(var i=0 ; i < metas.length ; i++) {

        if ( metas[i].name === "csrf-token") {

            return  metas[i].content;       
        }
    }  

})();

// adding constant into our app

yourAngularApp.constant('CSRF_TOKEN', csrftoken); 

Angular のデフォルトの http ヘッダーをセットアップする必要があります。csrf トークンを Angular のヘッダーに追加しましょう

/*
 * App Configs
 */
blog.config(['$httpProvider', 'CSRF_TOKEN',

  function($httpProvider, CSRF_TOKEN) {


    /**
     * adds CSRF token to header
     */
    $httpProvider.defaults.headers.common['X-CSRF-TOKEN'] = CSRF_TOKEN;

 }]);

最後に、laravel 側でこの変更を行うための新しいフィルターが必要です。

Route::filter('csrfInHeader', function($route, $request) {

    if (Session::token() !== (string) $request->header('X-CSRF-TOKEN') ) {

        throw new Illuminate\Session\TokenMismatchException;

    }
});

「csrfInHeader」フィルターは、angular アプリによるすべての http リクエストをチェックします。各リクエストごとに csrf トークンを追加する必要はありません。さらに、Karma でアプリをテストする場合、テストで csrf トークンを取得する必要はありません。

于 2015-01-17T11:38:30.443 に答える