24

我正在尝试构建一个 angular + laravel rest 应用程序。我可以获得我的数据库的视图。当我尝试添加新项目时。我500 error告诉我不匹配的 csrf 令牌。我的表单布局是:

<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:像这样向 post 请求添加标头不会产生错误。

  $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

Rubens Mariuzzo 接受的解决方案有效,但是我认为我找到了一个我认为更好的替代解决方案。

这样,您不必将 html 脚本中的数据传递到您的 angularjs 应用程序中,并且可以更好地分离关注点。例如,这允许您将 Laravel 应用程序作为一个 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 回答
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 回答
7

我认为我的解决方案不那么痛苦,而且更灵活,尤其是它认为在 Karma 上测试你的应用程序。

首先将此代码添加到您的主视图

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

我们已将 csrf 令牌保存到 html 内容中,而无需添加路由。

现在我们通过 CSRF 令牌保护 AngularJs App 的所有请求

/**
 * 
 * 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 回答
1

最简单的方法是

Route::get('/getToken','Controller@getToken');

在您的 web 或 api.php 文件中

在控制器中

public function getToken(){

          return csrf_token();

    }

放置此代码

在 Angular 应用程序中

$http.get("http://localhost:8000/getToken")
  .then(function(response) {
    alert(response.data)
  });

获取 csrf_token() 的安全方法

于 2019-05-11T23:16:20.693 回答