Auth过滤器重定向回Laravel中的原始POST请求

时间:2013-09-19 16:27:15

标签: authentication laravel laravel-4 http-post

似乎Redirect :: guest('login')仅适用于GET请求。 IE浏览器。它会将经过身份验证的用户重定向到原始目标网址(GET)。

在有POST请求的情况下,在用户成功登录后,auth过滤器是否有办法继续POST到URL?

一个简单的例子:我想要显示一个可供任何人查看的表单。点击提交按钮后,auth过滤器将启动,这将使访客进入登录页面。身份验证成功后,我希望提交请求(即POST请求)继续进行。

2 个答案:

答案 0 :(得分:10)

我有同样的愿望,即使用原始输入重定向回POST请求。除了通过GET重定向到预期的URL之外,我找不到在Laravel中执行此操作的现有方法。

Laravel 5

我首先在Laravel 4中按照下面的大纲解决了这个问题,但发现完全相同的设置无法在Laravel 5中工作。按照Laravel 4的大纲,而不是创建IntendedUrlServiceProvider创建一个中间件。

  1. 问题是,在Laravel 5中,会话似乎是在所有ServiceProviders之后运行的StartSession开始的。
  2. /app/Http/Middleware/IntendedUrl.php

    <?php namespace App\Http\Middleware;
    
    use Closure;
    use Request;
    use Session;
    
    class IntendedUrl {
    
        /**
         * This loads saved POST input data and changes the method to POST if a visitor tried to access a page
         * but was blocked via an auth filter. Auth filter saves data via the Redirect::guest() and after
         * login it needs to be repopulated to simulate a POST.
         *
         * GET requests also may pass through here. I am less certain if it is required for them but shouldn't hurt
         * and may help load any input data.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            // Check to see if we were redirected to this page with the Redirect::intended().
            //      We extended the class to track when the redirect occurs so we know to reload additional request data
            if (Session::has('intended.load')) {
                // intended.load could be set without these being set if we were redirected to the default page
                //      if either exists, both should exist but checking separately to be safe
                if (Session::has('intended.method')) {
                    Request::setMethod(Session::get('intended.method'));
                }
                if (Session::has('intended.input')) {
                    Request::replace(Session::get('intended.input'));
                }
                // Erase all session keys created to track the intended request
                Session::forget('intended');
    
                // Laravel 5.2+ uses separate global and route middlewares. Dispatch altered request as the route type changed. *Credit to Munsio in answer below
                return \Route::dispatch($request);
            }
    
            return $next($request);
        }
    
    }
    
    1. 然后在下面的步骤4中添加IntendedUrlServiceProvider而不是在/app/Http/Kernel.php的$ middleware数组中的StartSession之后添加新的中间件
    2. protected $middleware = [
          'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
          'Illuminate\Cookie\Middleware\EncryptCookies',
          'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
          'Illuminate\Session\Middleware\StartSession',
          'Illuminate\View\Middleware\ShareErrorsFromSession',
      
          'App\Http\Middleware\IntendedUrl',
      ];
      
      • 另外值得注意的是,仅仅为了组织,我将我的客户服务提供商转移到新的标准/应用程序/提供商并更改了他们的名称空间。

      Laravel 4

      我决定扩展框架以添加此功能。我的完整解决方案很难详细说明,但这里有一个大纲。要做到这一点,您需要非常熟悉框架并阅读如何扩展它。 http://laravel.com/docs/extending#ioc-based-extension

      我还引用了泰勒的书“从学徒到工匠的Laravel”

      1. 扩展Redirector类以记录有关预期请求的其他信息。

        <?php namespace GQ\Routing;
        class Redirector extends \Illuminate\Routing\Redirector {
            /**
             * ** Extended to add functionality for restoring POST input and the POST method after a login
             */
            public function guest($path, $status = 302, $headers = array(), $secure = null)
            {
                // Recording the method and input for the request so that it can be reloaded after being redirected back to the intended page
                $this->session->put('intended.method', $this->generator->getRequest()->getMethod());
                $this->session->put('intended.input', $this->generator->getRequest()->all());
        
                return parent::guest($path, $status, $headers, $secure);
            }
        
            /**
             * ** Extended to record in the session when we redirect to an intended page so method and input can be loaded on the next page
             */
            public function intended($default = '/', $status = 302, $headers = array(), $secure = null)
            {
                $redirect_response = parent::intended($default, $status, $headers, $secure);
        
                // Set the intended.load session variable so we know we returned to the intended page and can load the additional method and input
                return $redirect_response->with('intended.load', true);
            }
        }
        ?>
        
      2. 创建一个新的服务提供者,该服务提供者在IOC容器中写入“重定向”。我最初尝试扩展RoutingServiceProvider,但是在使用它时遇到了麻烦。

        <?php namespace App\Providers;
        
        use GQ\Routing\Redirector;
        use Illuminate\Support\ServiceProvider;
        class RedirectServiceProvider extends ServiceProvider {
        
            protected $defer = true;
        
            /**
             * Register the Redirector service.
             *
             * ** Copy of class registerRedirector from RoutingServiceProvider,
             * using a different "use" statement at the top to use the extended Redirector class
             * Extending the RoutingServiceProvider was more of a pain to do right since it is loaded as a base provider in the Application
             *
             * @return void
             */
            public function register()
            {
                $this->app['redirect'] = $this->app->share(function($app)
                {
                    $redirector = new Redirector($app['url']);
        
                    // If the session is set on the application instance, we'll inject it into
                    // the redirector instance. This allows the redirect responses to allow
                    // for the quite convenient "with" methods that flash to the session.
                    if (isset($app['session.store']))
                    {
                        $redirector->setSession($app['session.store']);
                    }
        
                    return $redirector;
                });
            }
            public function provides() {
                return array('redirect');
            }
        }
        
      3. 创建一个新的服务提供者,它将在重定向后设置预期的方法和输入。

        <?php
        
        namespace GQ\Providers;
        
        use Illuminate\Support\ServiceProvider;
        
        class IntendedUrlServiceProvider extends ServiceProvider {
            /**
             * Bootstrap the application events.
             *
             * @return void
             */
            public function boot() {
                // Check to see if we were redirected to this page with the Redirect::intended().
                //        We extended the class to track when the redirect occurs so we know to reload additional request data
                if (\Session::has('intended.load')) {
                    // intended.load could be set without these being set if we were redirected to the default page
                    //        if either exists, both should exist but checking separately to be safe
                    if (\Session::has('intended.method')) {
                        \Request::setMethod(\Session::get('intended.method'));
                    }
                    if (\Session::has('intended.input')) {
                        \Request::replace(\Session::get('intended.input'));
                    }
                    // Erase all session keys created to track the intended request
                    \Session::forget('intended');
                }
            }
        
            public function register() {
            }
        }
        
      4. 最后在app / config / app.php中将您的2个新服务提供商添加到您的提供者数组

        'GQ\Providers\RedirectServiceProvider',
        'GQ\Providers\IntendedUrlServiceProvider',
        
      5. 希望这会引导你朝着一个好方向前进。这对我有用,但我还没有广泛测试过。也许如果它继续运作良好,我们可以构建一个作曲家包或获得Laravel中包含的能力。

答案 1 :(得分:1)

在Laravel 5.2中,他们实现了中间件组,对于新项目,他们将默认的“web”组应用于整个routes.php文件。

问题:
在确定路由后调用组中间件,这样只需更改当前请求的方法就没有效果。

有两种不同的方法让它恢复工作(我建议nr.2)


解决方案1:
将会话和预期的url中间件放回到Kernel.php文件中的全局中间件数组 - 这很容易并且可以工作,但有时你在项目旁边有一些REST-API路由,并且会话无关。


解决方案2:
将预期的url类放在Web组中的ShareErrorsFromSession类之后,并采用如下所示的文件:

// Erase all session keys created to track the intended request
Session::forget('intended');

$response = Route::dispatch($request);
return $response;

通过调度已修改的请求,我们打破了当前的生命周期并调用了新的生命周期,因此使用了正确的路由并且它按预期工作。 第二种方法使我们还可以根据需要为选定的路径定义已配置的网址功能。