身份验证过滤器重定向回 Laravel 中的原始 POST 请求

Posted

技术标签:

【中文标题】身份验证过滤器重定向回 Laravel 中的原始 POST 请求【英文标题】:Auth filter redirecting back to original POST request in Laravel 【发布时间】:2013-09-24 20:43:12 【问题描述】:

似乎 Redirect::guest('login') 仅适用于 GET 请求。 IE。它会将经过身份验证的用户重定向到原始预期的 URL (GET)。

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

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

【问题讨论】:

您可以使用Redirect::intended('route') 重定向到预期的路由,如果预期的路由不存在,它将重定向到intended 方法中指定的“路由” intended() 适用于 GET 请求,但 POST 请求呢? intended() 检查会话中预期的 url,因此对于 POST 请求,您可以设置预期的 url,例如 Session::put('url.intended', URL::full()); 我也遇到了这个问题。如果我没看错,就像我自己一样,作者不想在用户登录后简单地重定向,这就是Redirect::intended('/') 所做的。相反,他想要求用户进行身份验证,然后提交 POST 请求和重定向。重定向不能做 POST 请求,所以设置预期的 URL 只是解决方案的一部分。 【参考方案1】:

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

Laravel 5

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

    问题在于,在 Laravel 5 中,会话似乎是使用 StartSession 启动的,它在所有 ServiceProviders 之后运行。

/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);
    


    然后,不要像下面的第 4 步那样添加 IntendedUrlServiceProvider,而是在 /app/Http/Kernel.php 的 $middleware 数组中的 StartSession 之后添加新的中间件
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',
];
另外值得注意的是,为了组织,我将我的客户服务提供商移至新标准 /App/Providers 并更改了他们的命名空间。

Laravel 4

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

我还参考了 Taylor 的书《Laravel 从学徒到工匠》

    扩展 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);
        
    
    ?>
    

    创建一个新的服务提供者,它会覆盖 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');
        
    
    

    创建一个新的服务提供者,它将在重定向后设置预期的方法和输入。

    <?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() 
        
    
    

    最后将你的 2 个新服务提供者添加到 app/config/app.php 中的 providers 数组中

    'GQ\Providers\RedirectServiceProvider',
    'GQ\Providers\IntendedUrlServiceProvider',
    

希望这能引导您朝着正确的方向前进。这对我有用,但我没有对它进行广泛的测试。也许如果它继续运行良好,我们可以构建一个作曲家包或获得 Laravel 中包含的功能。

【讨论】:

使用 Laravel5,在 config/app.php 中启用 RoutingServiceProvider 会导致每条路由都抛出 404。有什么想法吗? 我回顾了我的代码并记得在我写完这个答案后不久,我最终没有像最初描述的那样扩展 RoutingServiceProvider,而是创建了一个新的 RedirectServiceProvider。我在 Laravel 4 下更新了我的第 2 步说明,这仍然是我在 Laravel 5 中拥有的相同文件。希望对您有所帮助。 @ZackHuston 干得好 :) 谢谢.. 基本测试完全适用于 version 5.1.33 (LTS)。通过阅读Why doesn't HTTP have POST redirect,我理解了为什么这没有在 Laravel 框架中实现,但我真的不在乎它,因为我的网站功能不是尽可能简单。再次感谢,如果使用此解决方案有任何影响,我会回来。 嗨,我正在使用 Laravel 5.1,我按照你的指南直到第 2 步,在$validation-&gt;fails() if($validation-&gt;fails()) // redirect()-&gt;back(); it's not work 之后我应该做什么【参考方案2】:

在 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;

通过调度修改后的请求,我们打破了当前的生命周期并调用了一个新的生命周期,因此使用了正确的路由并按预期工作。 第二种方法还使我们可以根据需要将意图的 url 功能仅定义到选定的路由。

【讨论】:

以上是关于身份验证过滤器重定向回 Laravel 中的原始 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

Servlet过滤器重定向,url没有改变

从 Web 应用程序中的 servlet 过滤器重定向到上一页

当 Auth 服务器重定向到 SSO 代理服务器时,它不知道登录是不是成功

如何使用浏览器重定向创建 Facebook 风格的应用内身份验证?

Laravel - 具有多重身份验证的未经身份验证的重定向问题

在 Laravel 5 中进行身份验证后重定向