会话超时后的 laravel csrf 令牌不匹配异常

Posted

技术标签:

【中文标题】会话超时后的 laravel csrf 令牌不匹配异常【英文标题】:laravel csrf token mismatch exception after session timeout 【发布时间】:2015-09-21 12:29:20 【问题描述】:

在我们的 laravel 5 应用程序中,登录是通过 ajax 进行的。如果用户在会话到期之前注销并重新登录,一切都很好。但是如果用户注销并在该页面上保持空闲直到会话过期,如果用户尝试重新登录,则会收到csrfTokenMismatch 异常。

我知道在verifyCsrfToken 中间件中,laravel 检查会话是否与 csrf 令牌匹配。同样在Guard.php logout() 方法中,会话将在注销时被清除。

所以我的问题是:

会话是否真的在注销时刷新,如果是这样,为什么用户仍然可以在我设置的会话过期之前重新登录?

会话过期时 csrf 令牌会发生什么?

最后,这个问题通常如何以优雅的方式处理?

提前致谢!

【问题讨论】:

【参考方案1】:

这个答案是参考 5.4 版本,可能是以前的版本,但我没有测试过。

问题的根源在于客户端的 CSRF 令牌已过期,这使得到服务器的任何 POST 都因该令牌而失败。

如果你使用 AJAX,你可以使用默认不做 CSRF 验证的 API 路由。

可以关闭特定 URI 的 CSRF 验证。在这种情况下,我将关闭 /logout 的 CSRF 验证。如果您确实想从验证中排除某些 URI,这种方法很有效。

app/Http/Middleware/VerifyCsrfToken.php

/**
 * 应该从 CSRF 验证中排除的 URI。
 *
 * @var 数组
 */
受保护的 $except = [
   '/登出'
];

此外,您应该以适合您的应用程序的方式处理 CSRF 错误。下面是一个你可以做的非常基本的事情的例子。

app/Exceptions/Handler.php

/**
 * 将异常渲染到 HTTP 响应中。
 *
 * @param \Illuminate\Http\Request $request
 * @param \异常 $异常
 * @return \Illuminate\Http\Response
 */
公共函数渲染($请求,异常$异常)

    if($exception instanceof \Illuminate\Session\TokenMismatchException)
        // 令牌不匹配是一个安全问题,请确保注销。
        验证::注销();

        // 告诉用户发生了什么。
        session()->flash('alert-warning','您的会话已过期。请登录以继续。');

        // 去登录。
        返回重定向()->路由('登录');
     

    返回父级::render($request, $exception);

顺便说一句,要测试这两个更改,您可以修改会话设置。我只是将生命周期设置为1 进行测试。然后,完成后将其重新设置(默认为 120)。您需要登录,加载表单页面,等待一分钟,然后尝试 POST。

配置/session.php

    /*
    |------------------------------------------------- -------------------------
    |会话生命周期
    |------------------------------------------------- -------------------------
    |
    |在这里您可以指定您希望会话的分钟数
    |允许在到期前保持空闲状态。如果你想要他们
    |要在浏览器关闭时立即过期,请设置该选项。
    |
    */

    '终生' => 1,

【讨论】:

【参考方案2】:

有一个名为 XSRF-Token 的 cookie,它的默认生存时间为 2 小时...

我通过修改 App/Exceptions/Handler.php 处理了这样的登录表单上的 TokenMissmatchExceptions:

// ....
use Illuminate\Session\TokenMismatchException;
// ....


public function render($request, Exception $e)

    if($e instanceof TokenMismatchException) 
        $uri = \Route::current()->uri();
        if($uri == "login") 
            return redirect()->route('your.login.route')
                             ->withErrors("Login Form was open too long. 
                                           Please try to login again");
        
    
    return parent::render($request, $e);

【讨论】:

【参考方案3】:

在你的情况下,我认为你会真正受益于使用这个:

https://github.com/GeneaLabs/laravel-caffeine

我个人处理这样的非 ajax 情况,您可以针对 Ajax 请求对其进行调整,以返回一些有用的 json 以进行错误处理:

public function render($request, Exception $e)
    
         if ($e instanceof \Illuminate\Session\TokenMismatchException) 

              return redirect()
                  ->back()
                  ->withInput($request->except('_token'))
                  ->withMessage('Your explanation message depending on how much you want to dumb it down, lol! ');

        

        return parent::render($request, $e);
    

【讨论】:

以上是关于会话超时后的 laravel csrf 令牌不匹配异常的主要内容,如果未能解决你的问题,请参考以下文章

Laravel + Vue.js (axios) - CSRF 令牌不匹配

laravel 7 csrf 令牌不匹配

为啥我在 laravel 中运行测试时收到“CSRF 令牌不匹配”?

在 laravel 5.5 的验证 csrf 令牌中没有收到错误令牌不匹配异常

Laravel 6 Passport CSRF 令牌不匹配

csrf 令牌会在 laravel 中的每个请求上自动重新生成,这会导致生产服务器上的 csrf 令牌不匹配