如何使用Laravel API在AngularJS表单中发送csrf_token()?
我正在尝试构build一个angular度+ laravelrest应用程序。 我可以得到我的数据库的意见。 当我尝试添加新项目。 我得到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); }); }
这是我用于我的应用程序的filter:
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' } });
一个选项是将CSRF令牌作为常量注入。 在您的标签中添加以下内容:
<script> angular.module("app").constant("CSRF_TOKEN", '{{ csrf_token() }}'); </script>
然后在你的模块方法,它可以在需要时注入。
app.factory("FooService", function($http, CSRF_TOKEN) { console.log(CSRF_TOKEN); };
也许您会对这个Laravel + AngularJS项目样例的源代码感兴趣。
Rubens Mariuzzo接受的解决scheme是可行的,但是我认为我find了一个我认为更好的替代解决scheme。
通过这种方式,您不必将html脚本中的数据传递到您的angularjs应用程序,并且可以更好地分离关注点。 例如,这可以让你把你的Laravel APP作为一个API。
我的解决scheme涉及通过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'
filter保护路由
// 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
filter
// 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路由都受到保护。
如果您使用Laravel 5,则不需要将CSRF令牌添加到Angular http头中。
与Angular Laravel 5自动为你做这个。
我认为我的解决scheme不那么痛苦,更加灵活,特别是它认为testing你的应用程序在噶玛。
首先添加这个代码您的主视图
<meta name="csrf-token" content="{{ csrf_token() }}">
我们已经将csrf标记保存到html内容中,而不添加路由。
现在我们通过CSRF令牌来保护AngularJs应用程序的所有请求
/** * * 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; }]);
最后,我们不得不需要新的filter来对laravel这个变化
Route::filter('csrfInHeader', function($route, $request) { if (Session::token() !== (string) $request->header('X-CSRF-TOKEN') ) { throw new Illuminate\Session\TokenMismatchException; } });
“csrfInHeader”filter将检查angular应用程序的所有HTTP请求。 您不需要为每个请求添加csrf标记。 此外,如果您通过Karmatesting您的应用程序,您将不会努力获取csrf标记进行testing。