【问题标题】:Laravel middleware 'except' rule not workingLaravel 中间件“除外”规则不起作用
【发布时间】:2016-04-15 03:33:11
【问题描述】:

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

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

“注销”规则(默认情况下存在)完美运行,但我添加的其他 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/facebookauth/facebook/callbackauth/facebook/unlink,我会被中间件拒绝并返回首页。

我已经尝试在/ 中指定“例外”规则,以便它们与routes.php 中的路线完全匹配,但没有区别。任何想法为什么这些规则被忽略,而默认的“注销”规则得到尊重?

干杯!

【问题讨论】:

    标签: laravel laravel-5 laravel-middleware


    【解决方案1】:

    您需要传递方法的名称而不是 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');
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

        'route/*'
        

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

        '/route'
        

        【讨论】:

          【解决方案4】:

          将中间件分配给一组路由时,您可能偶尔需要阻止将中间件应用于组内的单个路由。您可以使用 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

          【讨论】:

            【解决方案5】:

            在你的控制器中使用这个函数:

            public function __construct()
                {
                    $this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
                }
            

            *将 page_name_1/2/3 替换为您的。

            对我来说它工作正常。

            【讨论】:

              【解决方案6】:

              我已经解决了这个问题,这就是我正在做的事情。 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',
                  ];
              

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

              【讨论】:

                【解决方案7】:

                您应该将函数名称传递给 'except'。

                这是我的一个项目中的一个示例:

                $this->middleware('IsAdminOrSupport', ['except' => [
                        'ProductsByShopPage'
                        ]
                    ]);
                

                这意味着中间件“IsAdminOrSupport”应用于此控制器的所有方法,但方法“ProductByShopPage”除外。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-04-02
                  • 1970-01-01
                  • 2020-03-01
                  • 2018-11-09
                  • 1970-01-01
                  • 2022-01-13
                  相关资源
                  最近更新 更多