解决 Laravel 中令牌不匹配错误的正确方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45223087/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 16:20:43  来源:igfitidea点击:

What is the right way to resolve token mismatch error in laravel?

laraveltokencsrfcsrf-protection

提问by orustammanapov

since I've updated laravel to 5.4 I constantly get:

自从我将 laravel 更新到 5.4 以来,我不断得到:

TokenMismatchException in VerifyCsrfToken.php line 68

TokenMismatchException in VerifyCsrfToken.php line 68

exception thrown. After some digging and reading through a whole lot of posts and github issues I've figured that my tokens aren't matching :). The point is that my laravel app sets the encrypted version of the token "XSRF-TOKEN" instead of its plain (X-CSRF-TOKEN) counterpart and the helper csrf_token()spits out the plain token hence mismatching tokens. Confusing though why documentation mentions X-XSRF-TOKEN when I get XSRF-TOKEN (missing X-) instead? So the questions are:

抛出异常。经过一番挖掘和阅读大量帖子和github问题后,我发现我的令牌不匹配:)。关键是我的 laravel 应用程序设置了令牌“XSRF-TOKEN”的加密版本,而不是它的普通(X-CSRF-TOKEN)对应物,并且助手csrf_token()吐出普通令牌,因此令牌不匹配。令人困惑的是,当我得到 XSRF-TOKEN(缺少 X-)时,为什么文档会提到 X-XSRF-TOKEN?所以问题是:

  1. Has the missing "X-" some meaning?
  2. How do I change the encrypted version of the token to the plain one? (regardless of the question #3)
  3. Should I even try to make a plain token out of the encrypted one or is it better to encrypt csrf_token()instead? (Does it even matter, since the connection is encrypted?)
  4. Is listing "XSRF-TOKEN" under $exceptedin the \MiddleWare\EncryptCookies.phpa viable option or could you suggest a better solution instead? (which sort of brings us back to the question #3)
  1. 缺少的“X-”有什么含义吗?
  2. 如何将令牌的加密版本更改为普通版本?(不管问题#3)
  3. 我什至应该尝试从加密的令牌中制作一个普通的令牌还是加密更好csrf_token()?(这是否重要,因为连接是加密的?)
  4. $excepted\MiddleWare\EncryptCookies.php可行的选项中列出“XSRF-TOKEN”还是您可以提出更好的解决方案?(这让我们回到问题#3)

I'm sorry for so many questions and thanks in advance!

我很抱歉有这么多问题,并提前致谢!

Edit

编辑

After rereading my questions several times I came to conclusion that they weren't clear enough and didn't complied with the title. My problem were the mismatching tokens and I thought having my doubts cleared would lead me to a solution, I thank @ThomasMoors for his patience and help. I will accept his answer, and although I've solved my problem a different way, it was his help that led me to the solution! I've additionally posted my own answer which describes how I've solved my issue to help others having similar issues!

在多次重读我的问题后,我得出的结论是他们不够清楚,也不符合标题。我的问题是不匹配的令牌,我认为清除我的疑虑会引导我找到解决方案,我感谢 @ThomasMoors 的耐心和帮助。我会接受他的回答,虽然我用不同的方式解决了我的问题,但正是他的帮助让我找到了解决方案!我还发布了我自己的答案,其中描述了我如何解决我的问题以帮助其他有类似问题的人!

回答by online Thomas

The function that checks the tokentries to find it (1)inside a request plainwith the key _token, if it does not find it, it will try to look inside (2)the request headers with the key X-CSRF-TOKEN. The token to match is stored inside the session, where the session lives depends on your config.

支票令牌功能试图找到它(1)请求中平原与关键_token,如果没有找到它,它会尝试看看里面(2)请求标题与关键X-CSRF-TOKEN。要匹配的令牌存储在会话中,会话所在的位置取决于您的 config

    /**
     * Determine if the session and input CSRF tokens match.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function tokensMatch($request)
    {
        $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
        if ( ! $token && $header = $request->header('X-XSRF-TOKEN'))
        {
            $token = $this->encrypter->decrypt($header);
        }
        return StringUtils::equals($request->session()->token(), $token);
    }

Q: Has the missing "X-" some meaning?

缺少的“X-”有什么含义吗?

A: Laravel stores the current CSRF token in a XSRF-TOKEN cookie that is included with each response generated by the framework. You can use the cookie value to set the X-XSRF-TOKEN request header.

This cookie is primarily sent as a convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the X-XSRF-TOKEN header.

:Laravel 将当前的 CSRF 令牌存储在 XSRF-TOKEN cookie 中,该 cookie 包含在框架生成的每个响应中。您可以使用 cookie 值来设置 X-XSRF-TOKEN 请求标头。

这个 cookie 主要是为了方便而发送的,因为一些 JavaScript 框架和库,如 Angular 和 Axios,会自动将其值放在 X-XSRF-TOKEN 标头中。

Q: How do I change the encrypted version of the token to the plain one? (regardless of the question #3)

如何将令牌的加密版本更改为明文版本?(不管问题#3)

A: As in the code above (from github) \Illuminate\Contracts\Encryption\Encrypterhas a function called decrypt()that does this

:如上面的代码(来自 github)\Illuminate\Contracts\Encryption\Encrypter有一个调用的函数decrypt()来执行此操作

Q: Should I even try to make a plain token out of the encrypted one or is it better to encrypt csrf_token() instead? (Does it even matter, since the connection is encrypted?)

我是否应该尝试从加密的令牌中制作一个普通令牌,还是最好加密 csrf_token() ?(这是否重要,因为连接是加密的?)

A: It does not matter, because the token has no data inside it, it is just an identifier of a legit request originating from your site and not some ajax/xhr call from another domain. read more on https://en.wikipedia.org/wiki/Cross-site_request_forgery

:没关系,因为令牌里面没有数据,它只是来自您站点的合法请求的标识符,而不是来自另一个域的一些 ajax/xhr 调用。阅读更多https://en.wikipedia.org/wiki/Cross-site_request_forgery

Q: Is listing "XSRF-TOKEN" under $excepted in the \MiddleWare\EncryptCookies.php a viable option or could you suggest a better solution instead? (which sort of brings us back to the question #3)

在 \MiddleWare\EncryptCookies.php 中的 $excepted 下列出“XSRF-TOKEN”是一个可行的选择,还是您可以提出更好的解决方案?(这让我们回到问题#3)

A: The token has no reason to be inside a cookie at any time, does it?

A: 令牌没有理由在任何时候都在 cookie 中,是吗?

UPDATE

更新

It seems that from 5.0 to 5.4 indeed something has changedin the function above. It looks like this now:

似乎从 5.0 到 5.4 确实在上面的函数中发生了一些变化。现在看起来像这样:

 protected function tokensMatch($request)
    {
        $token = $this->getTokenFromRequest($request);
        return is_string($request->session()->token()) &&
               is_string($token) &&
               hash_equals($request->session()->token(), $token);
    }

回答by orustammanapov

Although I can't pinpoint the source of my problem (due to lack of experience with the framework) I could finally solve my issue. What I've missed so far is that some middleware classeswere moved to a different middleware group in the app/Http/Kernel.phpfile (my problem probably appeared because order in which they're loadedhave changed, or maybe because some of them weren't loaded at all?). I'm pretty sure, that it was the cause of my problem, because I've constantly reloaded my page along with clearing out the cache like suggested on github, it was suggested by jeremykenedy. The suggestion was to call the following commands:

虽然我无法查明问题的根源(由于缺乏框架经验),但我终于可以解决我的问题。到目前为止,我错过的是一些中间件类被移到了app/Http/Kernel.php文件中的不同中间件组(我的问题可能出现是因为它们的加载顺序发生了变化,或者可能是因为某些他们根本没有加载?)。我很确定,这是我的问题的原因,因为我一直在重新加载我的页面,并按照github 上的建议清除缓存,这是 jeremykenedy 建议的。建议是调用以下命令:

sudo rm -rf storage/framework/sessions/*
sudo php artisan cache:clear
sudo php artisan clear-compiled
sudo composer dump-autoload

which all of you tackling a similar problem have probably encountered hundred of times already. Another suggestion I often found was to change permissions on the storage folder, it might help to some of you tackling a similar problem!

你们所有解决类似问题的人可能已经遇到过数百次。我经常发现的另一个建议是更改存储文件夹的权限,它可能有助于你们中的一些人解决类似的问题!