以下是我如何让我的 CSRF 在我最近升级为使用 Laravel 5 的 jQuery Mobile 应用程序中的所有不同场景中工作的一些摘录:
我在变量中添加了一个加密的 csrf 令牌,该变量将传递给我的主基本控制器中的视图:
app\Http\Controllers\MyController.php
$this->data['encrypted_csrf_token'] = Crypt::encrypt(csrf_token());
然后,我在主视图标题中添加了元标记:
resources\views\partials\htmlHeader.blade.php
<meta name="_token" content="{!! $encrypted_csrf_token !!}"/>
然后,我还按照一些论坛的建议添加了这个 jquery sn-p:
$(function () {
$.ajaxSetup({
headers: {
'X-XSRF-TOKEN': $('meta[name="_token"]').attr('content')
}
});
});
但是,关键(至少对于我的设置而言)是在我的自定义 VerifyCsrfToken 中间件中添加了对 XSRF-TOKEN cookie 的检查:
app\Http\Middleware\VerifyCsrfToken.php:
/**
* Determine if the session and input CSRF tokens match.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function tokensMatch($request)
{
$token = $request->session()->token();
$header = $request->header('X-XSRF-TOKEN');
$cookie = $request->cookie('XSRF-TOKEN');
return StringUtils::equals($token, $request->input('_token')) ||
($header && StringUtils::equals($token, $this->encrypter->decrypt($header))) ||
($cookie && StringUtils::equals($token, $cookie));
}
在我添加之前,由于TokenMismatchException,我几乎所有的 AJAX POST(包括表单提交和延迟加载列表视图)都失败了。
编辑:
再想一想,我不确定将会话令牌与 cookie 中的集合进行比较有多大意义(这首先来自会话令牌,对吧?)。这可能只是绕过了这一切的安全性。
我认为我的主要问题是上面的 jquery sn-p 应该将 X-XSRF-TOKEN 标头添加到每个 ajax 请求中。在我为插件本身添加一些选项之前,这在我的 jQuery Mobile 应用程序中(特别是在我的 lazyloader plugin 中)对我不起作用。我添加了一个新的默认选择器csrf(在这种情况下为meta[name="_token"])和一个新的默认设置csrfHeaderKey(在这种情况下为X-XSRF-TOKEN)。基本上,在插件初始化期间,如果 csrf 选择器(默认或用户定义)可定位一个新的 _headers 属性,则使用 CSRF 令牌初始化。然后,在可以触发 ajax POST 的 3 个不同位置(重置会话变量或延迟加载列表视图时),$.ajax 的 headers 选项设置为 _headers 中的任何内容。
无论如何,由于服务器端收到的 X-XSRF-TOKEN 来自加密的元 _token,我认为 CSRF 保护现在可以正常工作。
我的app\Http\Middleware\VerifyCsrfToken.php 现在看起来像这样(这基本上回到了 Laravel 5 提供的默认实现 - LOL):
/**
* Determine if the session and input CSRF tokens match.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function tokensMatch($request)
{
$token = $request->session()->token();
$_token = $request->input('_token');
$header = $request->header('X-XSRF-TOKEN');
return StringUtils::equals($token, $_token) ||
($header && StringUtils::equals($token, $this->encrypter->decrypt($header)));
}