Laravel 5.6 实现 URL 自动登陆

翻译 Insua ⋅ 于 2个月前 ⋅ 最后回复由 独步天堂 57分钟前 ⋅ 2001 阅读 ⋅ 原文地址

站点的翻译文章创建时,您将第一时间收到通知。

这是一篇社区协同翻译的文章,已完成翻译,更多信息请点击 协同翻译介绍

自动登录

当用户回到你的应用程序时,自动登录是一个很好的方法让用户和你的应用保持紧密联系。如果你能防止他们从可能忘记密码的认证流程中分散注意力,或者甚至通过他们加入你网站的电子邮件地址来访问你的网站,那么他们可以继续做他们想做的事情,用户也会为此感到高兴。

我使用过的最常见的用例就是客户通知。 无论是电子邮件还是文本,如果你发送一个能够重新回到你网站的私密链接,你可以将其设置为重新登录到他们的账号 (如果他们还没有登录)。

Explorer 翻译于 2个月前

查看其他 1 个版本

旧的方式

我创建了一个 watson/autologin 包去实现这个特性-这个包用来生成一个用户自动登录的链接。这个包依赖一个数据库的表,表里面存储着还没到期的 URL 的 token 以及重定向的链接和要登录的用户信息。

新的方式

在 Laravel 5.6.12 中,有一个 称作为 signed URLS 的新的不错的特性 ,这个自带签名认证 URL 可以很简单的实现 URL 自动登录并且不需要依赖数据的存储。

miss201 翻译于 2个月前

让我们来看一个关于如何实现 URL 自动登录的简单例子。首先,我们先要创建一个用户自动登录的路由,并在这个路由上加上 signed 中间件。如果 URL 的签名不匹配,这个中间件将会抛出一个错误。在下面的例子中,我们让用户认证通过,登录,最后重定向到 home 。

use App\User;
use Illuminate\Support\Facades\Auth;

Route::get('/autologin/{user}, function (User $user) {
  Auth::login($user);

  return redirect()->home();
})->name('autologin')->middleware('signed');

生成 URL 也很简单,你可以使用 URL 的门面模式去生成,跟其他 URL 的生成方式是一样的。

use App\User;
use Illuminate\Support\Facades\URL;

$user = User::first();

$url = URL::signedRoute('autologin', ['user' => $user]);

上面的代码就会生成你所期望的 URL ,只是在 URL 上会带有一个很长的“随机”签名作为查询参数。就像这样子 /autologin/1?signature=someReallyLongString 。当 Laravel 去解析这个路由的时候, signed 中间件会先去查看并且验证这个签名是否有效,只有中间件这里验证通过了,程序才会继续往下面执行。

miss201 翻译于 2个月前

如果你想设置自动登录链接的有效期,其实这个功能也是开箱即用的。比如说你可以拥有一个自动登录的链接,这个链接一天以后将会失效。

但是要注意一点,如果你设置了自动登录链接的有效期,就要去考虑添加一些异常处理 Illuminate\Routing\Exceptions\InvalidSignatureException (当收到不合法的签名时,将会抛出异常)并且要通知你的用户,让他们知道是这个链接过期了而不是直接返回一个 状态码为 500 错误响应。

use App\User;
use Illuminate\Support\Facades\URL;

$user = User::first();

$url = URL::temporarySignedRoute(
  'autologin',
  now()->addDays(1),
  ['user' => $user]
);
miss201 翻译于 2个月前

工作原理?

实际上非常值得潜下心来看一看,它的底层是如何工作的,因为它的实现非常简单。让我们看这个 signedRoute 方法,忽略其它内容直接看最后一行。

/**
 * 为命名路由创建一个签名路由URL。
 *
 * @param  string  $name
 * @param  array  $parameters
 * @param  \DateTimeInterface|int  $expiration
 * @return string
 */
public function signedRoute($name, $parameters = [], $expiration = null)
{
    $parameters = $this->formatParameters($parameters);

    if ($expiration) {
        $parameters = $parameters + ['expires' => $this->availableAt($expiration)];
    }

    $key = call_user_func($this->keyResolver);

    return $this->route($name, $parameters + [
        'signature' => hash_hmac('sha256', $this->route($name, $parameters), $key),
    ]);
}

这个方法真正地返回你想要的 URL,并且带有附加查询参数 signature ,查询参数是通过把路由本身和应用的加密秘钥以 SHA-256 算法哈希加密处理的方式获得的。

JeremyKuang 翻译于 2个月前

这个 APP_KEY 是你在 .env 文件中设置的参数,用来内部加密。除了你之外没有人知道 APP_KEY 的值,所以其他人不可能给你的路由通过 SHA 加密生成一个合法的签名。

很不错-但接下来会发生什么?

如果我们去查看下 Illuminate\Routing\Middleware\ValidateSignature 这个中间件,里面没有很多的代码,但是它很简洁和清晰。

if ($request->hasValidSignature()) {
    return $next($request);
}

throw new InvalidSignatureException;

这个中间件会要求确认请求的签名是否合法,如果是合法的,则调用堆栈中的下一个中间件。如果不是合法的, 则这个请求不通过,并且抛出一个 InvalidSignatureException 的异常,阻止黑客去攻击这些看起来很招摇的 URL 。

miss201 翻译于 2个月前

.

最后让我们来看下 hasValidSignature 代码实现,代码不少,不过我们只需要专注几个重点即可。

/**
 * 检测请求是否有正确的签名
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
public function hasValidSignature(Request $request)
{
    $original = rtrim($request->url().'?'.http_build_query(
        Arr::except($request->query(), 'signature')
    ), '?');

    $expires = Arr::get($request->query(), 'expires');

    $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));

    return  hash_equals($signature, $request->query('signature')) &&
           ! ($expires && Carbon::now()->getTimestamp() > $expires);
}

首先重建去除了签名参数的路由,下一步对其进行 SHA-256 哈希,并将其值与 Laravel 生成的哈希签名做验证,如果哈希相等就算通过。

你可以看到,此函数甚至检查 expires 参数,这是为  temporarySignedRoute 的逻辑准备的。

感谢框架作者 Taylor 新增了这个简单的而优雅的功能。

Summer 翻译于 2个月前

另外

是否觉得奇怪,请求时中间件调用了 hasValidSignature() 方法,它实际属于 Illuminate\Routing\UrlGenerator? 这实际证明,它在 Laravel 的 FoundationServiceProvider 服务中被注册为宏。

Request::macro('hasValidSignature', function () {
    return URL::hasValidSignature($this);
});

不错的收获.

JeremyKuang 翻译于 2个月前

原文地址:https://www.neontsunami.com/posts/autolo...

译文地址:https://laravel-china.org/topics/13018/l...


本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

本帖已被设为精华帖!
回复数量: 4
  • Summer 站长 @ laravel-china.org

    此方案用来实现如登录电脑版 QQ,点击 QQ 邮箱或者 QQ 空间 icon ,浏览器打开相关页面并且直接是登录状态。

    2个月前 4
  • @Summer 做成二维码手机扫一扫还能在手机上或者App上登录。加个长连接+二维码还能在手机App上确认登录Web页。

    2个月前 1
  • @Summer 看了这篇文章才发现 Signed URLs 在5.6的文档中没有。文档翻译内容缺失啊,而且好像不只是这一篇的问题,应该是5.6后续版本升级新增的内容在中文文档中没有同步新增,建议翻译对着官方文档再校对一下。

    2个月前 3
  • @雪风 支持,这么好的功能,希望能有中文翻译

    2个月前
您需要登陆以后才能留下评论!

Composer 中国全量镜像

Top 250 扩展包

Lumen 中文文档

Laravel 速查表

Laravel 中文文档

Laravel 项目开发规范

Laravel 开发环境部署

Composer 中文文档

Elasticsearch-PHP 中文文档

Lumen 中文文档

GraphQL PHP 中文文档

社区文档撰写指南

TDD 构建 Laravel 论坛笔记

PHP PSR 标准规范

PHP 设计模式全集

Dingo API 中文文档