使用 Laravel Livewire 和 Laravel Fortify 进行登录

Posted

技术标签:

【中文标题】使用 Laravel Livewire 和 Laravel Fortify 进行登录【英文标题】:Use Laravel Livewire with Laravel Fortify for Login 【发布时间】:2021-02-19 13:10:48 【问题描述】:

我想使用 Laravel Fortify 和 Livewire 创建一个非常简单的登录表单。我不想使用 Jetstream,因为它有更多我不需要的功能和我需要的功能。

我在整个应用程序中都使用 livewire,并希望将它用于我的登录页面以提供实时即时验证。

我遇到的问题是,如果我在输入上使用wire:model,我将无法提交带有值的表单。

我也不能使用auth()->attempt(),因为它被Fortify劫持了。(至少,我认为是。众所周知,当我使用它时,它会返回false

如何在 fortify 中使用 livewire?我需要将 livewire 组件中的数据发送到POST /login

这是我目前的设置:

FortifyServiceProvider.php

public function boot()
    
        Fortify::createUsersUsing(CreateNewUser::class);
        Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
        Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
        Fortify::resetUserPasswordsUsing(ResetUserPassword::class);

        // Custom Fortify Views =============================

        // Login Page
        Fortify::loginView(function () 
            return view('auth.login');
        );
   

我的auth/login.blade.php(只需使用适当的布局模板调用 livewire 组件)

<x-layouts.auth>
    @livewire('auth.login')
</x-layouts.auth>

livewire组件livewire/auth/login.blade.php

<form wire:submit.prevent="login" action="#" method="POST">
    <div>
        <label for="email">Email address</label>
        <input wire:model="email" id="email" type="email" required autofocus>
        @error('email') $message @enderror
    </div>

    <div>
        <label for="password">Password</label>
        <input wire:model.lazy="password" id="password" type="password" required>
        @error('password') $message @enderror
    </div>

    <div>
        <input wire:model.lazy="remember" id="remember_me" type="checkbox">
        <label for="remember_me">Remember me</label>

        <a href="#">Forgot your password?</a>
    </div>

    <div>
        <button type="submit">Sign in</button>
    </div>
</form>

还有我的Http/Livewire/Auth/Login.php livewire 组件文件:

class Login extends Component

    public $email = '';
    public $password = '';
    public $remember = false;

    protected $rules = [
        'email' => 'required|email|exists:users,email',
        'password' => 'required',
    ];

    /**
     * Shows Real time validation for email field
     */
    public function updatedEmail() 
        $this->validate(['email' => 'email|exists:users,email']);
    

    /**
     * Logs user in when form is submitted
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Support\MessageBag
     */
    public function login()
    
        // ******* Need to submit the form to Fortify here?? ******
    

    /**
     * Renders the view
     * @return mixed
     */
    public function render()
    
        return view('livewire.auth.login')
            ->layout('layouts.auth');
    

【问题讨论】:

【参考方案1】:
class Login extends Component

  public $email = '';
  public $password = '';
......

首先,根据Livewire documentation,敏感信息(如密码)不应该用作livewire组件中的公共属性。因此,在使用带有用户身份验证的 livewire 之前,您应该三思。

protected $rules = [
  'email' => 'required|email|exists:users,email',
  'password' => 'required',
];

接下来,在验证规则中,您将使用 exists:user,email,如果电子邮件在数据库中,它将实时验证。我建议不要在生产系统中将此验证与 livewire 实时验证规则一起使用,因为恶意行为者很容易使用它来提取系统用户的电子邮件列表。 话虽如此,您可以通过多种方式在登录功能中进行身份验证(但不使用 Fortify)。例如:

class LoginForm extends Component

public $email = '';
public $password = '';
public $remember = false;

protected $rules = [
    'email' => 'required|email|exists:users,email',
    'password' => 'required',
];

/*

Sometimes it's useful to validate a form field as a user types into it. 
Livewire makes "real-time" validation simple with the $this->validateOnly() method.

To validate an input field after every update, we can use Livewire's updated hook:

 */
public function updated($propertyName)

    $this->validateOnly($propertyName);


public function render()

    return view('livewire.loginForm');


public function login()

    $this->validate();

    // Execution doesn't reach here if validation fails.

    $email = $this->email;
    $password = $this->password;
    $credentials = ['email'=>$email, 'password'=>$password];

    if (Auth::attempt($credentials)) 
        // Auth success
    

    else
        // Auth failed
    

再一次,当使用此方法时,您只是使用 Fortify 来检索登录页面,这绝对不是 Fortify 的预期用途。因此,在考虑了所有问题之后,我认为最好避免使用 livewire 进行用户身份验证过程。

【讨论】:

感谢您指出安全隐患以及在 Fortify 中使用传统 POST 表单完成身份验证的原因。我在 Fortify 的文档中对此进行了解释。 @codingFriend1 您是否在此评论中针对我? @AliAli 完全没有,除非是您编写了 Jetstream 文档(但在这种情况下,这并不是一种冒犯,而是一种善意的建议)我真的很感谢您对这个问题的回答很多,也赞成。我只是认为安全考虑比实际解决方案更重要。此外,在我上面的评论中,我实际上是指 LiveWire 文档的 Jetstream,而不是 Fortify 文档。【参考方案2】:

我尝试安装一个只有 Livewire 和 Fortify 的新项目来模拟您的场景。 经过长时间的思考,这是我的发现

    当我看到 login() 方法和 开始认为此方法将处理身份验证或 转发它什么的。 Fortify 使用自己的路由,因此您无法在没有的情况下进行身份验证 将表单数据发送到“/login”端点。 您的 livewire 组件将帮助显示实时 验证错误,(也许您可以禁用表单提交按钮 直到验证通过)。 让 Fortify 为您处理身份验证并将您的表单设置为 提交

&lt;form method="POST" action=" route('login') "&gt;

    如果您需要自定义身份验证过程,Fortify 提供 你这个功能很容易,看documentation 如果你坚持使用 login() 方法我认为你可以创建一个 PHP POST 请求发送字段,但我看不出这样做的意义。

我希望这会给你这个想法。

【讨论】:

所以最后,您不能在登录页面中使用完整页面的 LiveWire 组件,而只能将单个 LiveWire 组件(如带有动态错误输出的表单本身)包含到常规登录刀片视图中,并且仍然将表单作为常规 POST 提交。我想整个事情只是令人困惑,因为 Jetstream 有一个使用 Fortify 的 LiveWire 版本,但实际上 Fortify 显然不是在考虑 LiveWire 的情况下构建的。因此,Jetstream 也使用常规的完整页面视图进行登录。 是的,确实,很混乱,但事实就是这样。【参考方案3】:

我在 web.php 中添加路由如下:

Route::get('/login',Login::class)->middleware('guest')->name('login');

那个 Login::class 是一个 livewire 组件,其中路径是 App\Http\Livewire\Auth\Login 。 在 livewire/auth/login.blade.php 中存在一个按操作的表单:

<form method="POST" action=" route('login') "> .... </form>

那个 route('login') by post 方法与 Fortify 相关。

【讨论】:

以上是关于使用 Laravel Livewire 和 Laravel Fortify 进行登录的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 与 JetStream 和 LiveWire 不渲染(尾风)

Laravel 和 Livewire:组件更改时如何执行 javascript 函数?

dompdf 没有从 Laravel 8 和 Livewire 视图下载 PDF

Laravel(Livewire)中的组件拆分和保留参数

只有买家可以在 Livewire:Laravel 中对产品进行评分

laravel - livewire 整页组件