Как отправить csrf_token() внутри формы AngularJS с использованием API Laravel?
Я пытаюсь создать приложение для отдыха angular + 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() }}" />
Как я могу отправить csrf_token, когда я использую html-формы?
Спасибо
Изменить 1:
Добавление заголовка к сообщению, как это, не дает ошибок.
$http({
method : 'POST',
url : '/shop',
data : $scope.itemEntry, // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
});
Ответы
Ответ 1
Опция будет включать токен CSRF в качестве константы. Добавьте в свой заголовок следующее:
<script>
angular.module("app").constant("CSRF_TOKEN", '{{ csrf_token() }}');
</script>
Затем в ваших модульных методах его можно вводить по мере необходимости.
app.factory("FooService", function($http, CSRF_TOKEN) {
console.log(CSRF_TOKEN);
};
Возможно, вам будет интересно посмотреть на исходный код этого пример проекта Laravel + AngularJS.
Ответ 2
принятое решение Rubens Mariuzzo работает, однако я думаю, что нашел альтернативное решение, которое, я думаю, лучше.
Таким образом, вам не нужно передавать данные из html script в ваше приложение angularjs, и есть лучшее разделение проблем. Например. Это позволяет вам использовать Laravel APP как API.
Мое решение включает в себя получение токена CSRF через запрос api и установку этого значения в качестве константы.
Кроме того, вместо того, чтобы вводить токен CSRF при необходимости, вы устанавливаете токен в заголовке по умолчанию, который будет проверяться сервером по любому HTTP-запросу API.
Пример показывает laravel, однако любая серьезная структура должна иметь возможность предложить что-то подобное.
Маршрут CSRF в LARAVEL:
// Returns the csrf token for the current visitor 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 вводится как заголовок во ВСЕХ HTTP-запросах из приложения AngularJS, а ВСЕ маршруты API защищены.
Ответ 3
Если вы используете Laravel 5, не нужно, чтобы добавить токен CSRF в заголовки Angular http.
Laravel 5 с Angular сделает это автоматически для вас.
http://laravel.com/docs/5.1/routing#csrf-x-xsrf-token
Ответ 4
Я думаю, что мое решение меньше боли и гораздо более гибкое, особенно оно думает, тестируя ваше приложение в карме.
Сначала добавьте этот код в ваш основной вид
<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);
Нам нужно настроить заголовки HTTP по умолчанию для Angular. Давайте добавим токен 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" будет проверять весь HTTP-запрос с помощью приложения Angular. Вам не нужно добавлять токен csrf для каждого запроса. Плюс, если вы проверите свое приложение по карме, вы не будете пытаться получить токен csrf при тестировании.
Ответ 5
Самый простой способ сделать это как
Route::get('/getToken','[email protected]');
В вашем веб файле или файле api.php
В контроллере
public function getToken(){
return csrf_token();
}
Поместите этот код
В угловом приложении
$http.get("http://localhost:8000/getToken")
.then(function(response) {
alert(response.data)
});
Безопасный способ получить csrf_token()