我试图利用Laravel 5.7中新的签名中间件,但是由于某种原因,生成的签名URL返回403无效签名。
我正在使用最新的Laravel版本以及PHP 7.2
这是我的web.php路由:
Route::get('/report/{user}/{client}', function ($user, $client) {
return ("El usuario es: $user y el cliente es: $client");
})->name('report.client')->middleware('signed');
这是在我的控制器中:
$objDemo->tempURL = Url::temporarySignedRoute('report.client', now('America/Panama')->addDays(5), [
'user' => 1,
'client' => 1
]);
URL已生成,并显示如下内容:
但是当我单击链接时,结果是403,并显示以下消息:“无效签名”
有什么想法吗?预先感谢
-----------更新------------
我已经做过的事情:
似乎什么都没用,总是得到403无效的签名页
-----------更新2 ------------
好,所以在进行一些挖掘和测试之后,我发现如果用户登录,laravel签名的路由将不起作用,这很奇怪,如果我注销则该路由可以正常工作,但是如果我登录了-然后显示403错误,这可能是因为Laravel在其他所有内容之后添加了会话Cookie标头吗?所以签名的路由会因此而失败?就是这样吗?
很奇怪,因为我想为用户创建一个临时链接以供用户下载内容,如果他们登录到我的Laravel应用中,他们将收到此403错误消息...:(
------------更新3 ------------------
我尝试在全新安装的laravel中运行并且完美运行,因此它来自我的主要Laravel应用,还尝试将每个作曲者依赖项安装到Laravel的全新安装中,无论用户登录状态如何,它仍然可以完美运行,因此这与我的依赖关系没有冲突。
答案 0 :(得分:1)
我只是遇到了这个问题,结果证明URL中的空参数永远不会生效。因此,当您这样做时:
URL::temporarySignedRoute('newsletter.verify', now()->addDays(3), ['name' => $name, 'email' => $email])
但是name是一个空字符串(因为它不是必需的),URL将作为查询字符串的一部分与name=
一起生成,但是此代码在Laravel中
$original = rtrim($url.'?'.Arr::query(Arr::except($request->query(), 'signature')), '?');
将不会返回空的name
,因此URL被“更改”并且验证失败。常用的中间件ConvertEmptyStringsToNull
可能与此有关。
答案 1 :(得分:1)
我在 APP_URL=http://localhost
文件中有 .env
。当我将值从服务器更改为 URL 时,问题就解决了。
我使用的是 Laravel 8+
。
答案 2 :(得分:0)
调试UrlGenerator :: hasValidSignature()之后,我以DD结束UrlGenerator.php
中的变量,如下所示:
public function hasValidSignature(Request $request, $absolute = true)
{
$url = $absolute ? $request->url() : '/'.$request->path();
//dd($url);
$original = rtrim($url.'?'.Arr::query(
Arr::except($request->query(), 'signature')
), '?');
dd($original);
$expires = Arr::get($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);
}
$original
变量向我显示了URL实际发生的情况,并显示了此信息:
https://example.com/report/1/1?expires=1546586977&settings%5Bincrementing%5D=1&settings%5Bexists%5D=1&settings%5BwasRecentlyCreated%5D=0&settings%5Btimestamps%5D=1&profile%5Bincrementing%5D=1&profile%5Bexists%5D=1&profile%5BwasRecentlyCreated%5D=0&profile%5Btimestamps%5D=1&user%5Bincrementing%5D=1&user%5Bexists%5D=1&user%5BwasRecentlyCreated%5D=0&user%5Btimestamps%5D=1
正如您所看到的,expires参数之后有一些参数,这些参数在创建路由之后被添加了,这就是问题所在,这是因为我有一个中间件向这样的视图共享了一些信息:
UserDataMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Setting;
use App\UserProfile;
use Illuminate\Support\Facades\View;
class UserData
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check()) {
$settings = Setting::where('user_id', Auth::user()->id)->first();
$profile = UserProfile::where('user_id', Auth::id())->first();
$user = Auth::user();
View::share('settings', $settings); //Another way to share variables, with the View::share
View::share('profile', $profile);
//Now we need to share owr variables trough the REQUEST to our controllers
$request->merge([
'settings' => $settings,
'profile' => $profile,
'user' => $user
]);
}
return $next($request);
}
}
该中间件位于中间件组中,所以希望这是一个问题,如果将来有人进行实验,那么它可以首先进行检查。
答案 3 :(得分:0)
如果您正在使用Heroku,AWS或任何其他使用LoadBalancer的服务。还要确保到达您的应用程序的代理受到信任。
有关更多信息,请参见this answer。
答案 4 :(得分:0)
尝试以下代码:
class TrustProxies extends Middleware
{
protected $proxies = '*';
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}
答案 5 :(得分:0)
基本上,您的签名不匹配,因为通过\ Illuminate \ Support \ Facades \ URL :: signedRoute生成的URL已由中间件更改,这意味着在检查$ request-> hasValidSignature()时,此返回false。
我有一个类似的问题,即SendGrid在我的电子邮件(&utm_campaign = website&utm_source = sendgrid.com&utm_medium = email)的URL中添加了UTM跟踪查询字符串,这改变了URL并最终改变了签名。
在我被黑客入侵时,我向控制器添加了以下代码,以去除其他查询参数并重新使用签名:
// Fix issue with sendgrid highjacking signed URL's with extra query params..
if ($request->query('utm_campaign')) {
$sig = $request->query('signature', '');
$url = route('route-key') . '?signature=' . $sig;
return redirect($url);
}
答案 6 :(得分:0)
我在黄昏时遇到了类似的问题, 是.env.dusk.testing中的APP_KEY与.env中的APP_KEY不匹配
答案 7 :(得分:0)
我遇到了同样的问题,并且发疯了,直到偶然发现@LaravDev的答案。
注意::我正在使用Laravel 7,它在web.php页面上有所不同
我的原始代码如下所示,它实际上只是向请求中添加了一个变量,以告诉我的视图不显示侧边栏。
Route::middleware(['noSidebar'])->group(function()
{
Auth::routes(['verify' => true]);
});
我必须删除Auth :: routes()短代码,并将其切换为完整的Auth路由堆栈。 (请注意,每个版本的Laravel都不一样)
Route::middleware(['noSidebar'])->group(function()
{
// Authentication Routes...
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
// Registration Routes...
Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
Route::post('register', 'Auth\RegisterController@register');
// Password Reset Routes...
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
// Confirm Password (added in v6.2)
Route::get('password/confirm', 'Auth\ConfirmPasswordController@showConfirmForm')->name('password.confirm');
Route::post('password/confirm', 'Auth\ConfirmPasswordController@confirm');
// Email Verification Routes...
Route::get('email/verify', 'Auth\VerificationController@show')->name('verification.notice');
Route::post('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');
});
//Moved the routes with tokens in the URL to outside my middleware grouping.
Route::get('email/verify/{id}/{hash}', 'Auth\VerificationController@verify')->name('verification.verify');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Tada可行! 谢谢大家
答案 8 :(得分:0)
了解 LARAVEL 电子邮件验证方式
了解验证方式可以帮助您轻松解决此错误。
laravel 使用 URL::temporarySignedRoute()
方法制作临时签名网址,
此方法在位于的 verificationUrl()
中调用
\vendor\laravel\framework\src\Illuminate\Auth\Notifications\VerifyEmail.php
。
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'verification.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
URL::temporarySignedRoute()
根据默认设置为 config('app.url)
的 .env('APP_URL')
制作网址。
所以如果发送到email的url和laravel在验证时(检查url签名的时候)得到的url不同,403 |出现无效签名。
示例:
APP_URL
设置为 http://yourdomain.com/
,验证链接应该类似于 http://yourdomain.com/email/verify/{id}/{hash}
。现在,如果您将服务器配置设置为重定向到 https
,则会发生无效签名,因为 Laravel 获取的 url 是 https://yourdomain.com/email/verify/{id}/{hash}
并且与电子邮件验证 url 不同。