身份验证控制器中的 Laravel 5.2 Web 中间件导致 csrf 令牌不匹配
Posted
技术标签:
【中文标题】身份验证控制器中的 Laravel 5.2 Web 中间件导致 csrf 令牌不匹配【英文标题】:Laravel 5.2 web middleware in auth controller causes csrf token mismatch 【发布时间】:2016-11-06 04:06:19 【问题描述】:当我像这样启用我的路由(登录、主页等)时,有人可以解释一下我的以下行为吗:
Route::group(['middleware' => 'web'], function ()
....
);
Ajax 登录模式工作正常,但是当我尝试以下我更喜欢使用的(在控制器中启用中间件)时:
class PagesController extends Controller
public function __construct()
$this->middleware('web');
...
class AuthController extends Controller
public function __construct()
$this->middleware('web');
$this->middleware('guest', ['except' => 'logout']);
...
VerifyCsrfToken.php 第 67 行中出现 TokenMismatchException。 据我所知,这两种方法应该没有区别,我在这里做错了什么?
csrf 令牌设置:
基本布局:
<meta name="csrf-token" content=" csrf_token() ">
模态js:
var options =
emulateJSON: true,
headers:
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
;
【问题讨论】:
您能否确认您没有两次运行 web 中间件? 我可以确认,当我从 authcontroller 构造函数中删除中间件 web 时,它不会返回不匹配但成功并且不会创建会话。 在 Laravel 5.2 中,web
中间件会自动应用于 routes.php
中的所有路由,因此您不应再次应用 web
中间件。
【参考方案1】:
我会给你一个有效的例子,从中汲取对你有帮助的想法:
app/Http/routes.php:
// all routes that start with: "/auth" are not filtered by any middleware
Route::group(['prefix' => 'auth'], function()
Route::get('/', ['as' => 'auth', 'uses' => 'AuthController@index']);
Route::post('/', ['as' => 'auth.attempt', 'uses' => 'AuthController@attempt']);
Route::delete('/', ['uses' => 'AuthController@destroy']);
Route::any('destroy', ['as' => 'auth.destroy', 'uses' => 'AuthController@destroy']);
);
// all routes that start with: "/billing" will be handled by this group (prefix => 'billing')
// all controllers inside this route group are located in 'Billing' namespace
// all routes in this group are pre-checked by middleware 'HasAccessToBilling'
Route::group(['prefix' => 'billing', 'namespace' => 'Billing', 'middleware' => ['App\Http\Middleware\HasAccessToBilling']], function()
Route::any('/', ['as' => 'billing', 'uses' => 'DashboardController@index']);
Route::get('profile', ['as' => 'billing.profile', 'uses' => 'ProfileController@index']);
// TARIFFS
Route::group(['prefix' => 'tariffs'], function()
Route::get('/', ['as' => 'billing.tariffs', 'uses' => 'TariffsController@index']); // showing page with tariffs paginated
Route::get('all', ['as' => 'billing.tariffs.all', 'uses' => 'TariffsController@all']); // listing all tariffs with json (see controller)
Route::get('create', ['as' => 'billing.tariffs.create', 'uses' => 'TariffsController@create']); // create form
Route::post('/', ['as' => 'billing.tariffs.store', 'uses' => 'TariffsController@store']); // creating
Route::get('id', ['as' => 'billing.tariffs.edit', 'uses' => 'TariffsController@edit']); // edit form
Route::post('id', ['as' => 'billing.tariffs.update', 'uses' => 'TariffsController@update']); // updating
Route::get('id/activate', ['as' => 'billing.tariffs.activate', 'uses' => 'TariffsController@activate']); // active = 1
Route::get('id/suspend', ['as' => 'billing.tariffs.suspend', 'uses' => 'TariffsController@suspend']); // active = 0
Route::get('id/delete', ['as' => 'billing.tariffs.delete', 'uses' => 'TariffsController@delete']); // deleted = 1
);
app/Http/Middleware/HasAccessToBilling.php:
<?php namespace App\Http\Middleware;
use App\Library\Auth;
use Closure;
use Illuminate\Http\Request;
class HasAccessToBilling
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
if (Auth::hasAccessTo('billing', $request))
return $next($request);
return redirect()->route('auth');
应用程序/库/Auth.php:
<?php namespace App\Library;
use \App\Models\User;
use Illuminate\Http\Request;
use Crypt;
class Auth
public static function recoverSession(Request $request)
$rememberToken = $request->cookie('remember-token', null);
if(is_null($rememberToken))
return null;
try
$rememberToken = Crypt::decrypt($rememberToken);
$auth = json_decode($rememberToken, true);
$request->session()->set('auth', $auth);
catch(\Exception $ex)
return $request->session()->get('auth');
public static function hasAccessTo($realm, Request $request)
$auth = $request->session()->get('auth', null);
if (is_null($auth))
$auth = self::recoverSession($request);
return (isset($auth['access_to']))?
in_array($realm, $auth['access_to'])
: false;
最后是示例控制器:
请参阅命名空间 'Billing' 必须与文件夹相同,否则您将在 composer 中进行手动类别名。
app/Http/Controllers/Billing/TariffsController.php:
<?php namespace App\Http\Controllers\Billing;
use Illuminate\Http\Request;
use Redirect;
use App\Http\Controllers\Controller;
use App\Models\Tariff as Model;
class TariffsController extends Controller
/**
* Listing records
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
$records = Model::paginate();
return view('billing.tariffs.index', compact('records'));
/**
* Listing all tariff plans as json
* @return \Illuminate\Http\JsonResponse
*/
public function all()
return $this->ok(Model::all());
总结:
如果您在 Route::group
中定义了中间件 - 因此无需在构造函数中调用中间件。路由组的想法是让你在编写路由、使用中间件等进行访问时免于重复代码。
如果不是 Intranet 的应用程序,则不能由拒绝公共访问的中间件保护身份验证控制器。所以你可以看到在我的路由文件中我没有为“/auth”前缀路由定义中间件。
我不使用 csrf 令牌(我看不出有任何理由,多年的工作我从来没有得到 csrf 帮助或挽救我生命的那一刻),所以我已经从Kernel.php
中删除它。
【讨论】:
以上是关于身份验证控制器中的 Laravel 5.2 Web 中间件导致 csrf 令牌不匹配的主要内容,如果未能解决你的问题,请参考以下文章