侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

在Laravel项目中的实现无密码认证之:发送邮箱链接授权

Laravel  /  管理员 发布于 2年前   499

laravel项目中无密码认证需求:

有时候,我们不希望用户有密码。

有时,我们想发送一个神奇的链接到用户的电子邮件地址,让他们点击以获得访问权。


解决方式:

定向邮箱发送授权链接

介绍一个你可以用来自己实现的流程,这个工作流程的重点是创建一个签名的URL,

让我们向用户的电子邮件地址发送一个特定的URL,而且只有这个人能够访问这个URL。


实现思路:

我们首先要从我们的迁移、模型和模型工厂中删除密码字段。

由于不需要,我们要确保删除它,因为它在默认情况下不是一个可忽略的列。

这是一个相对简单的实现过程,所以我不会为这部分展示任何代码示例。

同时,我们可以删除密码重置表,因为我们将没有密码需要重置。


路由应该是我们接下来要看的东西。

我们可以把登录路由创建为一个简单的视图路由,因为我们将在这个例子中使用Livewire。

让我们来看看如何注册这个路由:

Route::middleware(['guest'])->group(static function (): void {
    Route::view('login', 'app.auth.login')->name('login');
});

我们想把这个包在访客中间件中,以便在用户已经登录的情况下强制重定向。

我不会去看这个例子的用户界面,但在教程的最后,有一个指向GitHub上的repo的链接。

让我们来看看我们将用于登录表单的Livewire组件。

final class LoginForm extends Component
{
    public string $email = '';
 
    public string $status = '';
 
    public function submit(SendLoginLink $action): void
    {
        $this->validate();
 
        $action->handle(
            email: $this->email,
        );
 
        $this->status = 'An email has been sent for you to log in.';
    }
 
    public function rules(): array
    {
        return [
            'email' => [
                'required',
                'email',
                Rule::exists(
                    table: 'users',
                    column: 'email',
                ),
            ]
        ];
    }
 
    public function render(): View
    {
        return view('livewire.auth.login-form');
    }
}

我们的组件有两个我们要使用的属性。

电子邮件是用来捕获表单输入的。

然后是状态,所以我们不需要依赖请求会话。

我们有一个方法可以返回验证规则。

这是我对Livewire组件中验证规则的首选方法。

我们的提交方法是这个组件的主要方法,这是我在处理表单组件时使用的一个命名惯例。

这对我来说很有意义,但请自由选择适合你的命名方法。

我们使用Laravels容器将一个动作类注入这个方法,以分享创建和发送签名URL的逻辑。

我们在这里需要做的就是把输入的电子邮件传递给动作,并设置一个状态,提醒用户正在发送电子邮件。


现在让我们来看看我们要使用的动作:

final class SendLoginLink
{
    public function handle(string $email): void
    {
        Mail::to(
            users: $email,
        )->send(
            mailable: new LoginLink(
                url: URL::temporarySignedRoute(
                    name: 'login:store',
                    parameters: [
                        'email' => $email,
                    ],
                    expiration: 3600,
                ),
            )
        );
    }
}

这个动作只需要发送一封电子邮件。

如果我们想的话,我们可以将其配置为队列 - 但在处理需要快速处理的动作时,如果我们正在建立一个API,最好是排队。

我们有一个名为LoginLink的可发送类,我们通过它来传递我们想要使用的URL。

我们的URL是通过传入我们想要生成的路由的名称和传入你想要作为签名的一部分的参数来创建的:

final class LoginLink extends Mailable
{
    use Queueable, SerializesModels;
 
    public function __construct(
        public readonly string $url,
    ) {}
 
    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'Your Magic Link is here!',
        );
    }
 
    public function content(): Content
    {
        return new Content(
            markdown: 'emails.auth.login-link',
            with: [
                'url' => $this->url,
            ],
        );
    }
 
    public function attachments(): array
    {
        return [];
    }
}

我们的可邮寄类是相对直接的,与标准的可邮寄类没有什么区别。

我们传入一个字符串作为URL。

然后,我们想把它传递给内容中的一个markdown视图:

<x-mail::message>
# Login Link
 
Use the link below to log into the {{ config('app.name') }} application.
 
<x-mail::button :url="$url">
Login
</x-mail::button>
 
Thanks,<br>
{{ config('app.name') }}
</x-mail::message>

用户将收到这封邮件并点击链接,将他们带到已签署的URL。

让我们注册这个路由,看看它看起来如何:

Route::middleware(['guest'])->group(static function (): void {
    Route::view('login', 'app.auth.login')->name('login');
    Route::get(
        'login/{email}',
        LoginController::class,
    )->middleware('signed')->name('login:store');
});

我们要为这个路由使用一个控制器,并确保我们添加签名的中间件。

现在让我们看看控制器,看看我们如何处理签名的URL:

final class LoginController
{
    public function __invoke(Request $request, string $email): RedirectResponse
    {
        if (! $request->hasValidSignature()) {
            abort(Response::HTTP_UNAUTHORIZED);
        }
 
        /**
         * @var User $user
         */
        $user = User::query()->where('email', $email)->firstOrFail();
 
        Auth::login($user);
 
        return new RedirectResponse(
            url: route('dashboard:show'),
        );
    }
}

我们的第一步是确保URL有一个有效的签名,如果它没有,我们要抛出一个未经授权的响应。

一旦我们知道签名是有效的,我们就可以查询通过的用户并对他们进行认证。

最后,我们返回一个重定向到仪表板。


我们的用户现在已经成功登录,我们的旅程已经完成。

然而,我们也需要看一下注册路线。

让我们接下来添加这个路由。

同样,这将是一个视图路由:

Route::middleware(['guest'])->group(static function (): void {
    Route::view('login', 'app.auth.login')->name('login');
    Route::get(
        'login/{email}',
        LoginController::class,
    )->middleware('signed')->name('login:store');
 
    Route::view('register', 'app.auth.register')->name('register');
});

同样,我们在注册表格中使用了一个Livewire组件--就像我们在登录过程中做的那样:

final class RegisterForm extends Component
{
    public string $name = '';
 
    public string $email = '';
 
    public string $status = '';
 
    public function submit(CreateNewUser $user, SendLoginLink $action): void
    {
        $this->validate();
 
        $user = $user->handle(
            name: $this->name,
            email: $this->email,
        );
 
        if (! $user) {
            throw ValidationException::withMessages(
                messages: [
                    'email' => 'Something went wrong, please try again later.',
                ],
            );
        }
 
        $action->handle(
            email: $this->email,
        );
 
        $this->status = 'An email has been sent for you to log in.';
    }
 
    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'min:2',
                'max:55',
            ],
            'email' => [
                'required',
                'email',
            ]
        ];
    }
 
    public function render(): View
    {
        return view('livewire.auth.register-form');
    }
}

我们捕获用户的名字,电子邮件地址,并有一个状态属性,而不是再次使用请求会话。

我们再次使用一个规则方法来返回这个请求的验证规则。

我们回到提交方法,这一次,我们要注入两个动作。


CreateNewUser是我们用来创建并返回一个基于所提供信息的新用户的动作。

如果这个动作因为某些原因而失败,我们会在邮件中抛出一个验证异常。

然后,我们使用我们在登录表单上使用的SendLoginLink动作,以减少代码重复:

final class CreateNewUser
{
    public function handle(string $name, string $email): Builder|Model
    {
        return User::query()->create([
            'name' => $name,
            'email' => $email,
        ]);
    }
}


以上就是实现无密码认证的许多方法之一,这确实是一种有效及便捷的方法。


  • 上一条:
    在go语言中使用GoRoutines实现高性能并发批量调用api示例
    下一条:
    最新国内免注册ChatGPT体验站_ChatGPT镜像站访问链接地址2023/3/28持续更新
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • Laravel 11.14版本发布 - 新的字符串助手和ServeCommand改进(0个评论)
    • Laravel 11.12版本发布 - Artisan的`make`命令自动剪切`.php `扩展(0个评论)
    • Laravel的轻量型购物车扩展包:binafy/laravel-cart(0个评论)
    • Laravel 11.11版本发布 - 查看模型中的第三方关系:show(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-07
    • 2017-08
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-09
    • 2020-10
    • 2020-11
    • 2021-01
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客