Laravel 中间件“除外”规则不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34711134/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 13:01:25  来源:igfitidea点击:

Laravel middleware 'except' rule not working

laravellaravel-5laravel-middleware

提问by jd182

I have a controller with the following in the constructor:

我在构造函数中有一个具有以下内容的控制器:

$this->middleware('guest', ['except' =>
    [
        'logout',
        'auth/facebook',
        'auth/facebook/callback',
        'auth/facebook/unlink'
    ]
]);

The 'logout' rule (which is there by default) works perfectly but the other 3 rules I have added are ignored. The routes in routes.phplook like this:

“注销”规则(默认情况下存在)运行良好,但我添加的其他 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');
}

If I visit auth/facebook, auth/facebook/callbackor auth/facebook/unlinkwhilst logged in I get denied by the middleware and thrown back to the homepage.

如果我访问auth/facebookauth/facebook/callback或者auth/facebook/unlink在登录时我会被中间件拒绝并返回主页。

I've tried specifying the 'except' rules with proceeding /'s so they match the routes in routes.phpexactly but it makes no difference. Any ideas why these rules are being ignored, whilst the default 'logout' rule is respected?

我已经尝试使用proceding /'s指定'except' 规则,以便它们routes.php完全匹配路由,但没有区别。任何想法为什么这些规则被忽略,而默认的“注销”规则被尊重?

Cheers!

干杯!

回答by Lucas Silva

You should inform the methods name instead URI.

您应该通知方法名称而不是 URI。

<?php

namespace App\Http\Controllers;

class MyController extends Controller {
    public function __construct() {
        $this->middleware('guest', ['except' => [
            'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
        ]]);
    }
}

回答by Tyler Pashigian

If you are trying to follow the Laravel Documentation, an alternative solution to this is suggested by adding routes to the $except variable in the /Http/Middleware/VerifyCsrfToken.php file. The documentation says to add them like this:

如果您尝试遵循 Laravel 文档,建议通过将路由添加到 /Http/Middleware/VerifyCsrfToken.php 文件中的 $except 变量来解决此问题。文档说要像这样添加它们:

'route/*'

But I found the only way to get it to work is by putting the routes to ignore like this:

但我发现让它工作的唯一方法是像这样忽略路由:

'/route'

回答by cmac

I solved this issue in my Middleware by adding this inExceptArrayfunction. It's the same way VerifyCsrfTokenhandles the except array.

我通过添加这个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);
    }
}

回答by agm1984

I have this solved, and here's what I am doing. Aso, I just realized this is very similar to what cmacdid in his answer.

我已经解决了这个问题,这就是我正在做的事情。Aso,我刚刚意识到这与cmac在他的回答中所做的非常相似。

api.php

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

登录控制器.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

验证.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) {
            // ...
        }

    }

I over-engineered it slightly. Today I only need an exemption on /api/logout, but I set the logic up to quickly add more routes. If you research the VerifyCsrfTokenmiddleware, you'll see it takes a form like this:

我稍微过度设计了它。今天我只需要对 进行豁免/api/logout,但我设置了逻辑以快速添加更多路线。如果您研究VerifyCsrfToken中间件,您会看到它采用如下形式:

    protected $except = [
        'api/logout',
        'api/foobars*',
        'stripe/poop',
        'https://www.external.com/yolo',
    ];

That's why I put that "note" in my doc above there. $request->path() === $excluded_routewill probably not match api/foobars*, but $request->is('api/foobars*')should. Additionally, a person might be able to use something like $request->url() === $excluded_routeto match http://www.external.com/yolo.

这就是为什么我把那个“注释”放在我的文档上面。$request->path() === $excluded_route可能不匹配api/foobars*,但$request->is('api/foobars*')应该匹配。此外,一个人可能能够使用类似$request->url() === $excluded_route来匹配http://www.external.com/yolo.