如何使用 Laravel API 在 AngularJS 表单中发送 csrf_token()?

Posted

技术标签:

【中文标题】如何使用 Laravel API 在 AngularJS 表单中发送 csrf_token()?【英文标题】:How to send csrf_token() inside AngularJS form using Laravel API? 【发布时间】:2013-08-22 14:26:13 【问题描述】:

我正在尝试构建一个 angular + laravel rest 应用程序。我可以获得我的数据库的视图。当我尝试添加新项目时。我收到 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() " />

当我使用 html 表单时,如何发送 csrf_token?

谢谢

编辑 1: 像这样向 post 请求添加标头不会产生错误。

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

【问题讨论】:

【参考方案1】:

一个选项是将 CSRF 令牌作为常量注入。在你的 head 标签中添加以下内容:

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

然后在你的模块方法中,它可以在需要时注入。

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

也许你会有兴趣偷看这个sample Laravel + AngularJS project的源代码。

【讨论】:

您好,我在头部添加了脚本。我注入了 CSRF_TOKEN 。如何在表单中添加它?顺便说一句,我编辑了我的代码。 我仍然无法管理它,我阅读了端到端的代码。我编辑了 $scope.addItem 。可以看看吗?【参考方案2】:

Rubens Mariuzzo 接受的解决方案有效,但我认为我找到了一个我认为更好的替代解决方案。

这样您就不必将 html 脚本中的数据传递到您的 angularjs 应用程序中,并且可以更好地分离关注点。例如。这允许您将 Laravel 应用程序作为一个 API。

我的解决方案是通过 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 =&gt; '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 常量作为标头注入到来自 AngularJS 应用的所有 http 请求中,并且所有 API 路由都受到保护。

【讨论】:

嘿,我为该解决方案添加了另一种选择。如果它也是一个不错的解决方案,您能否检查并发表您的评论? @ytsejam - 我不明白如何在标头中指定内容类型是从服务器获取 CSRF 令牌到 AngularJS 应用程序的替代解决方案。 我跟着这个教程scotch.io/tutorials/php/… @Gravy 您是否从同一个域为您的 Angular 应用程序提供服务?我目前在 api.example.com 有我的 api,并且从 example.com 提供了 angular,所以我在进行会话时遇到问题,因为它被认为是跨源的。 这不适用于 REST api,令牌不会相同。【参考方案3】:

如果你使用 Laravel 5,不需要将 CSRF 令牌添加到 Angular http headers。

带有 Angular 的 Laravel 5 会自动为您完成这项工作。

http://laravel.com/docs/5.1/routing#csrf-x-xsrf-token

【讨论】:

太棒了。完全错过了这个【参考方案4】:

我认为我的解决方案更轻松,更灵活,尤其是它认为在 Karma 上测试您的应用程序。

首先将此代码添加到您的主视图

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

我们已将 csrf 令牌保存到 html 内容中,而无需添加路由。

现在我们通过 CSRF 令牌保护 AngularJs App 的所有请求

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

我们需要为 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;

 ]);

最后,我们必须在 laravel 侧需要新的过滤器来进行此更改..

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

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

        throw new Illuminate\Session\TokenMismatchException;

    
);

"csrfInHeader" 过滤器将通过 Angular 应用检查所有 http 请求。您不需要为每个请求添加 csrf 令牌。另外,如果您通过 Karma 测试您的应用程序,您将不会努力在测试中获取 csrf 令牌..

【讨论】:

【参考方案5】:

最简单的方法

Route::get('/getToken','Controller@getToken');

在您的 web 或 api.php 文件中

在控制器中

public function getToken()

          return csrf_token();

    

放置此代码

在 Angular 应用中

$http.get("http://localhost:8000/getToken")
  .then(function(response) 
    alert(response.data)
  );

获取 csrf_token() 的安全方法

【讨论】:

以上是关于如何使用 Laravel API 在 AngularJS 表单中发送 csrf_token()?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 4 中使用 RESTful API? [关闭]

如何使用 CSRF 令牌发布到 Laravel API?

如何将 Laravel 项目转换为 laravel api?

Laravel 上的 Mailchimp API,如何在路由中使用它?

如何使用外部 api 在 Laravel 应用程序中工作?

如何使用 AngularJS 保护 Laravel 4 中的 API 访问