我最近将我的项目从Laravel 5.6更新到5.7,并向我的项目添加了Laravel文档描述的电子邮件验证步骤。 一切在我的开发机器(http)上都运行良好,但是当我用所有更改更新生产服务器(https)时,然后当laravel发送时 我收到的电子邮件带有链接(已签名的路由),它为我生成了单击按钮或粘贴到我的浏览器中的链接,laravel似乎无法验证其创建的签名。副作用是每次我单击按钮或将链接粘贴到浏览器中时,我都会收到错误消息:
403很抱歉,您无权访问此页面。
到目前为止,我一直在查找laravel的ValidateSignature.php类中的代码,并添加了一些日志消息。
public function handle($request, Closure $next)
{
Log::info('checking signature');
if ($request->hasValidSignature()) {
Log::info('signature is valid');
return $next($request);
}
Log::info('throwing InvalidSignatureException');
throw new InvalidSignatureException;
}
更具体地说,我在laravel单元UrlGenerator.php中跟踪了确切的问题 我通过以下方法添加了日志:
public function hasValidSignature(Request $request)
{
$original = rtrim($request->url().'?'.Arr::query(
Arr::except($request->query(), 'signature')
), '?');
$expires = Arr::get($request->query(), 'expires');
$signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
Log::info('url: '.$original);
Log::info('expire: '.$expires);
Log::info(' new signature: '.$signature);
Log::info('link signature: '.$request->query('signature', ''));
Log::info('hash equals: '.hash_equals($signature, $request->query('signature', '')));
Log::info('expired: '.!($expires && Carbon::now()->getTimestamp() > $expires));
return hash_equals($signature, $request->query('signature', '')) &&
! ($expires && Carbon::now()->getTimestamp() > $expires);
}
当我单击按钮或在浏览器中粘贴链接并按Enter时,我收到以下日志消息: (出于明显的原因,我更改了我的真实域名。...不要尝试营销我的网站或其他东西)
checking signature
url: http://www.example.com/email/verify/2?expires=1538012234
expire: 1538012234
new signature: 1326b9e7402a51e0f05ddf1cb14f1e14852b4c5f0d1d6e726554806e7d85b4b1
link signature: e1d3ad5dc88faa8d8b0e6890ef60e216b75d26ef7ed5c6ab1cc661548e0ad8df
hash equals:
expired: 1
throwing InvalidSignatureException
所以我不知道该错误是在laravel创建初始签名的逻辑中还是在尝试对其进行验证的逻辑中。 但是就像我说的那样,这一切在我的开发机器上都很好用。我已经清除了缓存,清除了路由,更新为最新代码,重新启动了服务器,所有我能想到的。 任何帮助将不胜感激。
****更新*****
我更深入地挖掘了问题,并缩小了范围。 我不敢相信我昨晚没看到这个。如果我们仔细查看一则日志消息上方列出的输出日志
url: http://www.example.com/email/verify/2?expires=1538012234
向我们展示了问题。因此,正如我之前所说,开发机器是http,但实时服务器是https。我今天早上(经过4个小时的睡眠后)看到日志显示,以某种方式,hasValidSignature()方法中的逻辑正在获取使用http而不是https的路由。因此,当我返回电子邮件时,电子邮件中的链接为https,如果我将该URL粘贴到浏览器中,则该URL为https,并且在此逻辑返回403错误后,在浏览器中,浏览器仍显示https。 因此,现在我们可以集中讨论如何将我的路由/ URL转换为http?我在这里真的很挣扎,因为我不知道该如何处理该url,因为/ email / verify甚至都没有在我的任何路由文件中列出(我知道),而且我不能说我知道在下面寻找什么要么为此蒙上了一层阴影,所以我真的希望在这里有所帮助。
这也是我的.env文件中的设置:
APP_USE_HTTPS=true
APP_URL=https://www.example.com
APP_ENV=production
在AppServiceProvider的启动方法中,我有
public function boot()
{
Schema::defaultStringLength(191);
if (env('APP_USE_HTTPS'))
{
Log::info('forcing URLs to use https');
\URL::forceScheme('https');
}
答案 0 :(得分:1)
如果在Apache代理后面有Laravel应用,也会发生这种情况。就我们而言,我们拥有或多或少相同的.env配置,并且我们也有
URL::forceScheme('https');
在我们的AppServiceProvider中。
这将创建以下URL:在对签名进行签名时: https://..../email/verify/174?expires=1556027661 验证签名时: http://..../email/verify/174
我们的解决方法是替换“签名”中间件:
在app / Http / Kernel.php中,使用'signed' => \App\Http\Middleware\ValidateHttpsSignature::class
,然后使用以下代码创建此类:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Exceptions\InvalidSignatureException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Carbon;
class ValidateHttpsSignature
{
var $keyResolver;
public function __construct()
{
$this->keyResolver = function () {
return App::make('config')->get('app.key');
};
}
/**
* gebaseerd op vendor/laravel/framework/src/Illuminate/Routing/Middleware/ValidateSignature.php
* maar zorgt er voor dat een url altijd als https behandeld wordt. dit fixt het feit dat
* laravel achter een rewrite proxy draait en urls binnenkrijgt als http.
*
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->hasValidSignature($request)) {
return $next($request);
}
throw new InvalidSignatureException;
}
/**
* Determine if the given request has a valid signature.
* copied and modified from
* vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php:363
* @param \Illuminate\Http\Request $request
* @param bool $absolute
* @return bool
*/
public function hasValidSignature(Request $request, $absolute = true)
{
$url = $absolute ? $request->url() : '/'.$request->path();
// THE FIX:
$url = str_replace("http://","https://", $url);
$original = rtrim($url.'?'.Arr::query(
Arr::except($request->query(), 'signature')
), '?');
$expires = $request->query('expires');
$signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
return hash_equals($signature, (string) $request->query('signature', '')) &&
! ($expires && Carbon::now()->getTimestamp() > $expires);
}
}