2013-08-20 5 views
19

Ich versuche eine eckige + Laravel Rest-Anwendung zu erstellen. Ich kann die Ansichten meiner Datenbank abrufen. Wenn ich versuche, neue Elemente hinzuzufügen. Ich bekomme 500 error sagt mir Mismatch csrf Token. Mein Formularlayout ist:Wie csrf_token() in AngularJS Formular mit Laravel API zu senden?

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

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

Dies ist, wie ich versuche, Artikel zur Datenbank hinzufügen:

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

} 

Hier ist mein Filter, die ich für meine Anwendung verwenden:

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

In meine Klinge Ansichten ich benutze dies und es funktioniert:

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

Wie kann ich das csrf_token senden, wenn ich HTML-Formulare verwende?

Dank

Edit 1: Header Anfrage zu schreiben Hinzufügen wie diese Fehler nicht geben.

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

Antwort

26

Eine Option wird sein, das CSRF-Token als Konstante zu injizieren. Fügen Sie Folgendes in Ihrem Kopf-Tag an:

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

Dann in Ihrem Modul Methoden kann es bei Bedarf injiziert werden.

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

Vielleicht werden Sie auf den Quellcode dieser sample Laravel + AngularJS project von spähen interessiert.

+0

Hallo, ich habe Skript in den Kopf hinzugefügt. Ich habe CSRF_TOKEN eingespritzt. Wie füge ich es in Form hinzu? Ich habe meinen Code geändert. – ytsejam

+0

Ich konnte es immer noch nicht schaffen, ich las den Code Ende-zu-Ende. Ich habe $ scope.addItem bearbeitet. Kannst du einen Blick darauf werfen? – ytsejam

20

die akzeptierte Lösung von Rubens Mariuzzo funktioniert, aber ich denke, dass ich eine alternative Lösung gefunden, die ich denke, besser ist.

Auf diese Weise müssen Sie eine bessere Trennung von Bedenken keine Daten aus dem HTML-Skript übergeben in Ihre AngularJS App und es gibt. Z.B. Dies ermöglicht Ihnen, Ihre Laravel-APP als nur eine API zu haben.

Meine Lösung beinhaltet, das CSRF-Token über eine API-Anforderung zu erhalten und diesen Wert als Konstante festzulegen.

Ferner können anstelle die CSRF-Token der Injektion, wenn nötig, stellen Sie die Token in einem Standard-Header, die vom Server auf jeder API HTTP-Anforderung erhalten überprüft würde.

Beispiel zeigt Laravel, jedoch sollte jeder ernsthafte Rahmen in der Lage sein, etwas ähnliches anzubieten.

CSRF Strecke in Laravel:

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

Schutz Routen mit dem 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'); 
}); 

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

Die AngularJS dies in app setzen Zeug.js:

Blocking Version:

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

nicht blockierenden Version

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

Nun ist die CSRF_TOKEN Konstante als Header in alle HTTP-Anfragen von der AngularJS App und alle API-Routen injiziert werden geschützt .

+0

Hey, ich habe eine weitere Alternative für diese Lösung hinzugefügt. Können Sie überprüfen und kommentieren, wenn es auch eine nette Lösung ist? – ytsejam

+0

@ytsejam - Ich verstehe nicht, wie die Angabe des Inhaltstyps in der Kopfzeile eine alternative Lösung darstellt, um ein CSRF-Token vom Server zur AngularJS-App zu bekommen. – Gravy

+0

Ich folgte diesem Tutorial http://scotch.io/tutorials/php/create-a-laravel-and-angular-single-page-comment-application – ytsejam

6

Ich denke, meine Lösung ist weniger Schmerzen und viel flexibler, vor allem denkt es, Ihre App auf Karma zu testen.

hinzufügen Erstens diesen Code Ihrer Masteransicht

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

Wir csrf Token in HTML-Inhalte ohne Route Hinzufügen gespeichert haben.

Jetzt schützen wir alle Anforderungen von AngularJS App von 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); 

Wir müssen Setup Standard-HTTP-Header für Angular. Lassen Sie uns unsere csrf Token-Header ist zu Angular hinzufügen

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

}]); 

Schließlich haben wir neuen Filter müssen für diese auf der Seite der Laravel ändert ..

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

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

     throw new Illuminate\Session\TokenMismatchException; 

    } 
}); 

"csrfInHeader" Filter alle HTTP-Anforderung durch Winkel App überprüfen . Sie müssen nicht jedes csrf-Token zu jeder Anfrage hinzufügen. Plus, wenn Sie Ihre Anwendung von Karma testen, werden Sie nicht Mühe zu CSRF-Token auf das Testen bekommen ..