如何为Laravel中的一组GET路由开启CSRF保护?
解决方案
推荐方案:将GET路由改为POST请求
Laravel的CSRF保护默认针对非幂等请求(如POST/PUT/DELETE等),因为GET请求语义上是用于获取数据、不修改服务器状态的操作,通常无需CSRF保护。如果你的这些接口实际会修改数据(或需要严格验证请求来源),最优雅的方式是遵循HTTP语义,将路由改为POST:
- 修改路由定义:
Route::post('/location/get', 'Ajax@getProducts'); Route::post('/products/get', 'Ajax@getProducts'); Route::post('/schedule/get', 'Ajax@getProducts');
- 前端请求时携带CSRF令牌:
- 表单提交时添加隐藏字段:
<input type="hidden" name="_token" value="{{ csrf_token() }}"> - AJAX请求(如Axios)时,确保页面头部有meta标签,Axios会自动读取并加入请求头:
<meta name="csrf-token" content="{{ csrf_token() }}">
- 表单提交时添加隐藏字段:
备选方案:为GET路由手动添加CSRF验证(仅用于无法改POST的场景)
如果因业务限制必须保留GET请求,可通过自定义中间件或控制器内验证的方式实现,避免修改框架核心逻辑:
方式1:自定义中间件验证CSRF
- 创建中间件:
php artisan make:middleware VerifyCsrfForGet
- 编写中间件验证逻辑:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Facades\Crypt; class VerifyCsrfForGet { public function handle($request, Closure $next) { $token = $request->query('_token'); try { // 验证令牌有效性与过期时间 Crypt::decrypt($token, true); } catch (DecryptException $e) { abort(419, 'CSRF令牌无效或已过期'); } return $next($request); } }
- 在
app/Http/Kernel.php注册中间件:
protected $routeMiddleware = [ // ...其他中间件 'csrf.get' => \App\Http\Middleware\VerifyCsrfForGet::class, ];
- 为目标路由绑定该中间件:
Route::get('/location/get', 'Ajax@getProducts')->middleware('csrf.get'); Route::get('/products/get', 'Ajax@getProducts')->middleware('csrf.get'); Route::get('/schedule/get', 'Ajax@getProducts')->middleware('csrf.get');
- 前端请求时在URL参数中携带令牌:
// Axios示例 axios.get('/location/get', { params: { _token: document.querySelector('meta[name="csrf-token"]').content } });
方式2:直接在控制器方法中验证
若不想创建中间件,可在Ajax@getProducts方法开头添加验证逻辑:
public function getProducts(Request $request) { try { Crypt::decrypt($request->query('_token'), true); } catch (DecryptException $e) { abort(419, 'CSRF令牌无效'); } // 原有业务逻辑代码 }
注意:强制为GET请求添加CSRF保护不符合HTTP语义规范,仅建议在特殊场景下使用,优先推荐改为POST请求。
内容的提问来源于stack exchange,提问作者Viewsonic
相关产品推荐
相关产品推荐

