我最近将我的项目从 Laravel 5.6 更新到 5.7,并将 Laravel 文档中描述的电子邮件验证步骤添加到我的项目中。
在我的开发机器(http)上一切都运行良好,但是当我用所有更改更新我的生产服务器(https)时,当 laravel 发送时
我收到带有链接(签名路由)的电子邮件,它为我生成了单击按钮或粘贴到我的浏览器中的 Laravel 似乎无法验证它创建的签名。副作用是每次我单击按钮或将链接粘贴到浏览器中时都会收到错误:
403 抱歉,您无权访问此页面。
到目前为止,我追踪到的是我在 laravel Validate Signature.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
所以我不知道 bug 是否存在于 laravel 创建初始签名的逻辑中,或者是在尝试验证它时。
然而就像我说的,这一切在我的开发机器上运行得很好。我已经清除了缓存,清除了路由,更新到最新代码,重新启动了服务器,我能想到的一切。
任何帮助将不胜感激。
**** 更新 *****
我更深入地挖掘并缩小了问题的范围。
我不敢相信我昨晚没有看到这个。如果我们仔细查看上面列出的一条日志消息的输出日志
url: http://www.example.com/email/verify/2?expires=1538012234
向我们展示了问题所在。正如我之前所说,我的开发机器是 http,但我的实时服务器是 https。今天早上(经过 4 个小时的良好睡眠后),我看到日志向我们显示,hasValidSignature() 方法中的逻辑以某种方式获取了 http 而不是 https 的路由。因此,当我返回电子邮件时,电子邮件中的链接是 https,如果我将 URL 粘贴到浏览器中,它会显示 https,并且在我的浏览器中,此逻辑返回 403 错误后,浏览器仍然显示 https。
现在我们可以关注我的路由/url 如何转换为 http?我在这里真的很挣扎,因为我不知道如何处理该网址,因为 /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');
}