如何使用Laravel API在AngularJS表单中发送csrf_token()?

时间:2013-08-20 13:45:38

标签: angularjs laravel csrf-protection

我正在尝试构建一个角度+ laravel休息应用程序。我可以获得我的数据库的视图。当我尝试添加新项目时。我得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: 像这样在帖子请求中添加标题不会产生错误。

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

5 个答案:

答案 0 :(得分:28)

一个选项是将CSRF令牌注入为常量。在头标记中附加以下内容:

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

然后在你的模块方法中,它可以在需要时注入。

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

也许您会对查看此sample Laravel + AngularJS project的源代码感兴趣。

答案 1 :(得分: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路径都受到保护。

答案 2 :(得分:21)

如果您使用Laravel 5,无需将CSRF令牌添加到Angular http标头。

使用Angular的Laravel 5会自动为您执行此操作。

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

答案 3 :(得分:6)

我认为我的解决方案不那么痛苦且更灵活,特别是它认为在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;

    }
});

&#34; csrfInHeader&#34; filter将通过angular app检查所有http请求。您不需要为每个请求添加csrf令牌。另外,如果你通过Karma测试你的应用程序,你将不会努力在测试中获得csrf令牌。

答案 4 :(得分:1)

最简单的方法

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

在您的网络或api.php文件中

在控制器中

public function getToken(){

          return csrf_token();

    }
  

放置此代码

在Angular应用中

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

获取csrf_token()的安全方法