Laravel 5.8 从已清除的会话中单击注销后显示“419 页面已过期”

Laravel 5.8 showing "419 Page Expired" after clicking logout from an already cleared session

我 运行 php artisan make:auth 命令,我将逐步解释之后我做了什么以了解场景,

问题是,这种情况可能会出现,我不想看到这个错误信息,点击注销后就注销,即使会话已过期。

注意:这个问题不是因为没有添加@csrf

嗯,这是一个明显的消息,您可以尝试为该页面制作更好的布局,但显示它仍然很好,以便用户知道发生了什么。如果您想以不同的方式处理它,您可以尝试重定向到登录页面。

所以在渲染方法中的 app\Exceptions\Handler.php 文件中添加:

if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
    return redirect()->route('login');
}

恕我直言,您可以尝试修改您的 app/Http/Middleware/VerifyCsrfToken.php 文件。 用这样的东西编辑 $except 属性:

class VerifyCsrfToken extends Middleware
{       
    protected $except = [
        'http://example.com/logout',
    ];
<a href="{{ route('logout') }}" class="dropdown-item notify-item"="event.preventDefault(); document.getElementById('logout-form').submit();">
    <i class="fa fa-power-off"></i>  <span>{{ __('Logout') }} </span>
    </a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
  @csrf
</form>

您在注销表单中遗漏了@csrf,所以只有您收到错误 419

在一个 Laravel 6 项目中,我最终将 VerifyCsrfTokenMiddleware 修改如下

如您所见,我只是将 logout 命名路由添加到排除列表中。

我重写了 __construct 函数,因为在初始化新变量时我们不能使用 route() 函数

<?php

namespace App\Http\Middleware;

use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * Indicates whether the XSRF-TOKEN cookie should be set on the response.
     *
     * @var bool
     */
    protected $addHttpCookie = true;

    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [

    ];

    /**
     * Create a new middleware instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Contracts\Encryption\Encrypter  $encrypter
     * @return void
     */
    public function __construct(Application $app, Encrypter $encrypter)
    {
        parent::__construct($app, $encrypter);
        $this->except = [
            route('logout')
        ];
    }
}

问题的解决方法比较简单,需要对 VerifyCsrfToken 中间件;

use Closure;


    public function handle($request, Closure $next)
    {
        if(!Auth::check() && $request->route()->named('logout')) {
        
            $this->except[] = route('logout');
            
        }
        
        return parent::handle($request, $next);
    }

通常这个文件只包含一个 $except 路由数组,应该从 csrf 中忽略。

在这段代码中,我们覆盖了 handle 方法并执行了两个检查。

  • 用户是访客(即未使用经过身份验证的会话),并且
  • 是注销路由

如果两者都为真,那么我们将 'logout' 添加到 except 数组。然后我们将控制权传递给核心 VerifyCsrfMiddleware,它识别数组中是否存在注销路由,并绕过检查。表单数据已正确发布,我们使用 LogoutResponse 进行了重定向。

用户没有看到错误页面。

通过这种方式检查,我们确保真正的注销请求仍然受到 CSRF Token 的保护。

您需要将 CSRF 令牌添加到您的表单中:

<form action="{{ route('logout') }}" method="POST">
    @csrf
    <button type="submit" class="btn nav-link">Logout</button>
</form>

Laravel 8.x Docs

永远不会太晚)

添加app/Http/Middleware/VerifyCsrfToken.php

    use Closure;

    public function handle($request, Closure $next)
    {
        if($request->route()->named('logout')) {
            if (auth()->check()) {
                auth()->logout();
            }

            return redirect('/');
        }
    
        return parent::handle($request, $next);
    }

我认为这些是最好的解决方案。