2013-08-20 7 views
19

Sto provando a costruire un'applicazione di riposo angolare + larvello. Posso ottenere le viste del mio database. Quando provo ad aggiungere nuovi oggetti. Ricevo 500 error indicandomi un token csrf non corrispondente. mio layout forma è:Come inviare csrf_token() all'interno del modulo AngularJS utilizzando l'API di Laravel?

<form class="form-horizontal" ng-submit="addItem()"> 

    <input type="text" ng-model="itemEntry" placeholder="Type and hit Enter to add item"> 
</form> 

Questo è come io cerco di aggiungere l'elemento alla base di dati:

$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); 
     }); 

} 

Ecco il mio filtro che uso per la mia domanda:

Route::filter('csrf', function() 
{ 
    if (Session::token() != Input::get('_token')) 
    { 
     throw new Illuminate\Session\TokenMismatchException; 
    } 
}); 

In le mie visualizzazioni lama io uso questo e funziona:

<input type="hidden" name="_token" value="{{ csrf_token() }}" /> 

Come posso inviare csrf_token quando uso i moduli html?

Grazie

Edit 1: aggiunta di intestazione di inviare richiesta come questo non dà errori.

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

risposta

26

Un'opzione è quella di iniettare il token CSRF come costante. Aggiungi il seguente tag:

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

Quindi nei metodi del modulo può essere iniettato quando necessario.

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

Forse sarete interessato di sbirciare il codice sorgente di questa sample Laravel + AngularJS project.

+0

Ciao, ho aggiunto lo script in testa. Ho iniettato CSRF_TOKEN. Come posso aggiungerlo nel modulo? Ho modificato il mio codice btw. – ytsejam

+0

Non riesco ancora a gestirlo, ho letto il codice end-to-end. Ho modificato $ scope.addItem. Puoi dare un'occhiata? – ytsejam

20

la soluzione accettata da Rubens Mariuzzo funziona, tuttavia penso di aver trovato una soluzione alternativa che ritengo sia meglio.

In questo modo non è necessario passare i dati dallo script html alla propria app angularjs e vi è una migliore separazione delle preoccupazioni. Per esempio. Questo ti permette di avere la tua APP Laravel come una semplice API.

La mia soluzione implica il recupero del token CSRF tramite una richiesta API e l'impostazione di questo valore come costante.

Inoltre, invece di iniettare il token CSRF quando necessario, si imposta il token in un'intestazione predefinita che verrebbe controllata dal server su qualsiasi richiesta http API.

L'esempio mostra laravel, tuttavia qualsiasi framework serio dovrebbe essere in grado di offrire qualcosa di simile.

CSRF Percorso in laravel:

// Returns the csrf token for the current visitor's session. 
Route::get('api/csrf', function() { 
    return Session::token(); 
}); 

Protezione Itinerari con il 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'); 
}); 

Il api.csrf filtro

// 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); 
} 
}); 

I AngularJS roba mettere questo in app.JS:

Versione di blocco:

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; 
}]); 

non-Blocking versione

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); 

Ora la costante CSRF_TOKEN viene iniettato come un colpo di testa in tutte le richieste HTTP da App AngularJS e tutte le rotte API sono protetti .

+0

Ehi ho aggiunto un'altra alternativa per quella soluzione. Puoi controllare e commentare se è una buona soluzione anche? – ytsejam

+0

@ytsejam - Non capisco come la specifica del tipo di contenuto nell'intestazione sia una soluzione alternativa per ottenere un token CSRF dal server all'app AngularJS. – Gravy

+0

Ho seguito questo tutorial http://scotch.io/tutorials/php/create-a-laravel-and-angular-single-page-comment-application – ytsejam

6

Penso che la mia soluzione sia meno dolorosa e molto più flessibile, specialmente pensa di testare la tua app su Karma.

In primo luogo aggiungere questo codice la visualizzazione maestro

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

Abbiamo salvato CSRF gettone nella contenuti HTML senza l'aggiunta di percorso.

Ora proteggiamo tutte le richieste di AngularJs App da 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://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); 

Abbiamo bisogno di intestazioni HTTP configurazione di default per angolare. Aggiungiamo la nostra token CSRF alle intestazioni di angolari

/* 
* 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; 

}]); 

Infine abbiamo bisogno di nuovo filtro per questo cambia sul lato della laravel ..

Route::filter('csrfInHeader', function($route, $request) { 

    if (Session::token() !== (string) $request->header('X-CSRF-TOKEN')) { 

     throw new Illuminate\Session\TokenMismatchException; 

    } 
}); 

filtro "csrfInHeader" controllerà tutte le richieste HTTP da app angolare . Non è necessario aggiungere un token csrf ad ogni richiesta. Inoltre, se si prova la vostra applicazione dal Karma, non sarà possibile per ottenere CSRF token test ..

Problemi correlati