personalizadas - ¿Cómo enviar csrf_token() dentro de la forma AngularJS usando Laravel API?
directivas personalizadas angularjs (4)
Estoy intentando construir una aplicación de descanso angular + laravel. Puedo obtener las vistas de mi base de datos. Cuando trato de agregar nuevos elementos. Recibo un 500 error
que me dice que no coincide con el token csrf. Mi diseño de formulario es:
<form class="form-horizontal" ng-submit="addItem()">
<input type="text" ng-model="itemEntry" placeholder="Type and hit Enter to add item">
</form>
Así es como trato de agregar un elemento a la base de datos:
$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);
});
}
Aquí está mi filtro que uso para mi aplicación:
Route::filter(''csrf'', function()
{
if (Session::token() != Input::get(''_token''))
{
throw new Illuminate/Session/TokenMismatchException;
}
});
En las vistas de mi blade, uso esto y funciona:
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
¿Cómo puedo enviar csrf_token cuando uso formularios html?
Gracias
Editar 1: Agregar encabezado para publicar una solicitud como esta no da errores.
$http({
method : ''POST'',
url : ''/shop'',
data : $scope.itemEntry, // pass in data as strings
headers : { ''Content-Type'': ''application/x-www-form-urlencoded'' }
});
Una opción será inyectar el token CSRF como una constante. Agregue lo siguiente en su etiqueta principal:
<script>
angular.module("app").constant("CSRF_TOKEN", ''{{ csrf_token() }}'');
</script>
Luego, en los métodos de su módulo, se puede inyectar cuando sea necesario.
app.factory("FooService", function($http, CSRF_TOKEN) {
console.log(CSRF_TOKEN);
};
Tal vez le interese echar un vistazo al código fuente de este proyecto Laravel + AngularJS .
Creo que mi solución es menos dolorosa y mucho más flexible, especialmente si piensa probar su aplicación en Karma.
Primero agrega este código a tu vista maestra
<meta name="csrf-token" content="{{ csrf_token() }}">
Hemos guardado token csrf en contenido html sin agregar ruta.
Ahora protegemos todas las solicitudes de AngularJs App por CSRF token
/**
*
* 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://.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);
Necesitamos configurar encabezados http predeterminados para Angular. Agreguemos nuestro token csrf a los encabezados de 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;
}]);
Finalmente tenemos que necesitar un nuevo filtro para estos cambios en el lado de laravel.
Route::filter(''csrfInHeader'', function($route, $request) {
if (Session::token() !== (string) $request->header(''X-CSRF-TOKEN'') ) {
throw new Illuminate/Session/TokenMismatchException;
}
});
El filtro "csrfInHeader" verificará todas las solicitudes http de la aplicación angular. No es necesario agregar token csrf a cada solicitud. Además, si prueba su aplicación mediante Karma, no se esforzará por obtener el token csrf en las pruebas.
la solución aceptada por Rubens Mariuzzo funciona; sin embargo, creo que he encontrado una solución alternativa que creo que es mejor.
De esta forma, no tiene que pasar datos del script html a su aplicación angularjs y hay una mejor separación de preocupaciones. Por ejemplo, esto le permite tener su aplicación Laravel como solo una API.
Mi solución consiste en obtener el token CSRF a través de una solicitud de API y establecer este valor como una constante.
Además, en lugar de inyectar el token CSRF cuando sea necesario, configura el token en un encabezado predeterminado que el servidor verificará en cualquier solicitud http de API.
El ejemplo muestra laravel, sin embargo, cualquier marco serio debería poder ofrecer algo similar.
Ruta CSRF en LARAVEL:
// Returns the csrf token for the current visitor''s session.
Route::get(''api/csrf'', function() {
return Session::token();
});
Protección de rutas con el filtro 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'');
});
El filtro 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);
}
});
Las cosas de AngularJS ponen esto en app.js:
Versión de bloqueo:
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;
}]);
Versión sin bloqueo
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);
Ahora la constante CSRF_TOKEN se inyecta como un encabezado en TODAS las solicitudes http de la aplicación AngularJS y TODAS las rutas API están protegidas.
Si usa Laravel 5, no es necesario agregar el token CSRF a los encabezados http angulares.
Laravel 5 con Angular lo hace automáticamente por usted.