Laravel 中间件 'except' 规则无效
Laravel middleware 'except' rule not working
我有一个控制器,在构造函数中包含以下内容:
$this->middleware('guest', ['except' =>
[
'logout',
'auth/facebook',
'auth/facebook/callback',
'auth/facebook/unlink'
]
]);
'logout' 规则(默认情况下存在)完美运行,但我添加的其他 3 条规则被忽略了。 routes.php
中的路线如下所示:
Route::group(['middleware' => ['web']],function(){
Route::auth();
// Facebook auth
Route::get('/auth/facebook', 'Auth\AuthController@redirectToFacebook')->name('facebook_auth');
Route::get('/auth/facebook/callback', 'Auth\AuthController@handleFacebookCallback')->name('facebook_callback');
Route::get('/auth/facebook/unlink', 'Auth\AuthController@handleFacebookUnlink')->name('facebook_unlink');
}
如果我在登录时访问 auth/facebook
、auth/facebook/callback
或 auth/facebook/unlink
,我会被中间件拒绝并返回主页。
我已经尝试在 /
中指定 'except' 规则,以便它们与 routes.php
中的路由完全匹配,但这没有区别。知道为什么忽略这些规则,而遵守默认的 'logout' 规则吗?
干杯!
您需要传递方法的名称而不是 URI。
<?php
namespace App\Http\Controllers;
class MyController extends Controller {
public function __construct() {
$this->middleware('guest', ['except' => [
'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
]]);
}
}
从Laravel 5.3开始,你可以使用fluent interface来定义控制器上的中间件,这看起来比使用多维数组更干净。
<?php
$this->middleware('guest')->except('redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink');
如果您尝试遵循 Laravel 文档,建议通过向 /Http/Middleware/VerifyCsrfToken.php 文件中的 $except 变量添加路由来解决此问题。文档说要像这样添加它们:
'route/*'
但我发现让它工作的唯一方法是像这样忽略路由:
'/route'
我通过添加这个 inExceptArray
函数在我的中间件中解决了这个问题。这与 VerifyCsrfToken
处理 except 数组的方式相同。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* Routes that should skip handle.
*
* @var array
*/
protected $except = [
'/some/route',
];
/**
* Determine if the request has a URI that should pass through.
*
* @param Request $request
* @return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// check user authed or API Key
if (!$this->inExceptArray($request)) {
// Process middleware checks and return if failed...
if (true) {
// Middleware failed, send back response
return response()->json([
'error' => true,
'Message' => 'Failed Middleware check'
]);
}
}
// Middleware passed or in Except array
return $next($request);
}
}
我已经解决了这个问题,这就是我正在做的。 Aso,我刚刚意识到这与 cmac 在他的回答中所做的非常相似。
api.php
Route::group(['middleware' => 'auth'], function () {
Route::get('/user', 'Auth\UserController@me')->name('me');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
});
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers, ThrottlesLogins;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
// ...
/**
* If the user's session is expired, the auth token is already invalidated,
* so we just return success to the client.
*
* This solves the edge case where the user clicks the Logout button as their first
* interaction in a stale session, and allows a clean redirect to the login page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$user = $this->guard()->user();
if ($user) {
$this->guard()->logout();
JWTAuth::invalidate();
}
return response()->json(['success' => 'Logged out.'], 200);
}
}
Authenticate.php
class Authenticate extends Middleware
{
/**
* Exclude these routes from authentication check.
*
* Note: `$request->is('api/fragment*')` https://laravel.com/docs/7.x/requests
*
* @var array
*/
protected $except = [
'api/logout',
];
/**
* Ensure the user is authenticated.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
try {
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route from auth check...");
return $next($request);
}
}
// code below here requires 'auth'
{ catch ($e) {
// ...
}
}
我稍微过度设计了它。今天我只需要 /api/logout
上的豁免,但我设置了快速添加更多路由的逻辑。如果您研究 VerifyCsrfToken
中间件,您会发现它采用如下形式:
protected $except = [
'api/logout',
'api/foobars*',
'stripe/poop',
'https://www.external.com/yolo',
];
这就是为什么我将 "note" 放在上面的文档中的原因。 $request->path() === $excluded_route
可能不匹配 api/foobars*
,但 $request->is('api/foobars*')
应该匹配。此外,一个人也许可以使用 $request->url() === $excluded_route
之类的东西来匹配 http://www.external.com/yolo
.
将中间件分配给一组路由时,您有时可能需要防止将中间件应用于组内的单个路由。您可以使用 withoutMiddleware 方法完成此操作:
use App\Http\Middleware\CheckAge;
Route::middleware([CheckAge::class])->group(function () {
Route::get('/', function () {
//
});
Route::get('admin/profile', function () {
//
})->withoutMiddleware([CheckAge::class]);
});
有关详细信息,请阅读 documentation laravel middleware
在您的控制器中使用此功能:
public function __construct()
{
$this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
}
*用你的替换 page_name_1/2/3。
对我来说它工作正常。
您应该将函数名称传递给 'except'。
这是我的一个项目中的示例:
$this->middleware('IsAdminOrSupport', ['except' => [
'ProductsByShopPage'
]
]);
这意味着中间件 'IsAdminOrSupport' 应用于此控制器的所有方法,方法 'ProductByShopPage' 除外。
我有一个控制器,在构造函数中包含以下内容:
$this->middleware('guest', ['except' =>
[
'logout',
'auth/facebook',
'auth/facebook/callback',
'auth/facebook/unlink'
]
]);
'logout' 规则(默认情况下存在)完美运行,但我添加的其他 3 条规则被忽略了。 routes.php
中的路线如下所示:
Route::group(['middleware' => ['web']],function(){
Route::auth();
// Facebook auth
Route::get('/auth/facebook', 'Auth\AuthController@redirectToFacebook')->name('facebook_auth');
Route::get('/auth/facebook/callback', 'Auth\AuthController@handleFacebookCallback')->name('facebook_callback');
Route::get('/auth/facebook/unlink', 'Auth\AuthController@handleFacebookUnlink')->name('facebook_unlink');
}
如果我在登录时访问 auth/facebook
、auth/facebook/callback
或 auth/facebook/unlink
,我会被中间件拒绝并返回主页。
我已经尝试在 /
中指定 'except' 规则,以便它们与 routes.php
中的路由完全匹配,但这没有区别。知道为什么忽略这些规则,而遵守默认的 'logout' 规则吗?
干杯!
您需要传递方法的名称而不是 URI。
<?php
namespace App\Http\Controllers;
class MyController extends Controller {
public function __construct() {
$this->middleware('guest', ['except' => [
'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
]]);
}
}
从Laravel 5.3开始,你可以使用fluent interface来定义控制器上的中间件,这看起来比使用多维数组更干净。
<?php
$this->middleware('guest')->except('redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink');
如果您尝试遵循 Laravel 文档,建议通过向 /Http/Middleware/VerifyCsrfToken.php 文件中的 $except 变量添加路由来解决此问题。文档说要像这样添加它们:
'route/*'
但我发现让它工作的唯一方法是像这样忽略路由:
'/route'
我通过添加这个 inExceptArray
函数在我的中间件中解决了这个问题。这与 VerifyCsrfToken
处理 except 数组的方式相同。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* Routes that should skip handle.
*
* @var array
*/
protected $except = [
'/some/route',
];
/**
* Determine if the request has a URI that should pass through.
*
* @param Request $request
* @return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// check user authed or API Key
if (!$this->inExceptArray($request)) {
// Process middleware checks and return if failed...
if (true) {
// Middleware failed, send back response
return response()->json([
'error' => true,
'Message' => 'Failed Middleware check'
]);
}
}
// Middleware passed or in Except array
return $next($request);
}
}
我已经解决了这个问题,这就是我正在做的。 Aso,我刚刚意识到这与 cmac 在他的回答中所做的非常相似。
api.php
Route::group(['middleware' => 'auth'], function () {
Route::get('/user', 'Auth\UserController@me')->name('me');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
});
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers, ThrottlesLogins;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
// ...
/**
* If the user's session is expired, the auth token is already invalidated,
* so we just return success to the client.
*
* This solves the edge case where the user clicks the Logout button as their first
* interaction in a stale session, and allows a clean redirect to the login page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$user = $this->guard()->user();
if ($user) {
$this->guard()->logout();
JWTAuth::invalidate();
}
return response()->json(['success' => 'Logged out.'], 200);
}
}
Authenticate.php
class Authenticate extends Middleware
{
/**
* Exclude these routes from authentication check.
*
* Note: `$request->is('api/fragment*')` https://laravel.com/docs/7.x/requests
*
* @var array
*/
protected $except = [
'api/logout',
];
/**
* Ensure the user is authenticated.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
try {
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route from auth check...");
return $next($request);
}
}
// code below here requires 'auth'
{ catch ($e) {
// ...
}
}
我稍微过度设计了它。今天我只需要 /api/logout
上的豁免,但我设置了快速添加更多路由的逻辑。如果您研究 VerifyCsrfToken
中间件,您会发现它采用如下形式:
protected $except = [
'api/logout',
'api/foobars*',
'stripe/poop',
'https://www.external.com/yolo',
];
这就是为什么我将 "note" 放在上面的文档中的原因。 $request->path() === $excluded_route
可能不匹配 api/foobars*
,但 $request->is('api/foobars*')
应该匹配。此外,一个人也许可以使用 $request->url() === $excluded_route
之类的东西来匹配 http://www.external.com/yolo
.
将中间件分配给一组路由时,您有时可能需要防止将中间件应用于组内的单个路由。您可以使用 withoutMiddleware 方法完成此操作:
use App\Http\Middleware\CheckAge;
Route::middleware([CheckAge::class])->group(function () {
Route::get('/', function () {
//
});
Route::get('admin/profile', function () {
//
})->withoutMiddleware([CheckAge::class]);
});
有关详细信息,请阅读 documentation laravel middleware
在您的控制器中使用此功能:
public function __construct()
{
$this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
}
*用你的替换 page_name_1/2/3。
对我来说它工作正常。
您应该将函数名称传递给 'except'。
这是我的一个项目中的示例:
$this->middleware('IsAdminOrSupport', ['except' => [
'ProductsByShopPage'
]
]);
这意味着中间件 'IsAdminOrSupport' 应用于此控制器的所有方法,方法 'ProductByShopPage' 除外。