支持 Laravel.io 的持续开发 →

使用“Hashed”类型自动哈希 Laravel 模型值

23 Jan, 2024 6 min read 198 views

简介

哈希是一种重要的安全理念,每个网页开发者都应该了解。它允许我们安全地将密码存储在数据库中。

本文不会介绍哈希是什么,如果您不熟悉它,可以查看我的Laravel中加密和哈希指南文章。

在这篇简短的文章中,我们将探讨如何在将这些模型值存储在数据库之前,自动将这些值哈希化。

手动哈希模型值

在您的Laravel代码中,您可能已经习惯了这样做,以手动哈希一个值

use App\Models\User;
use Illuminate\Support\Facades\Hash;

$user = User::create([
    'name' => 'Ash',
    'email' => '[email protected]',
    'password' => Hash::make('password'),
]);

运行上述代码后,新用户的password字段将存储在数据库中,类似于以下内容

$2y$10$Ugrfp6Myf9zVOo66KEuF9uQZ3hyg3T5GhJNgjOTy7o7AXCXSpwWpy

自动哈希模型值

但是,如果我们想在Laravel的User模型中自动哈希password字段,而不必每次手动哈希,我们应该怎么做?

为了做到这一点,我们可以使用Laravel提供的、自Laravel v10.10.0版本开始支持的hashed模型转换。这将在值存储到数据库之前自动对字段值进行哈希处理。

让我们想象一下,我们想要更新上面的代码示例,并删除对password字段的手动哈希。我们首先需要在我们的App\Models\User类中指定我们想要为password字段使用hashed模型转换。我们可以通过如下方式更新模型的casts属性

declare(strict_types=1);

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

final class User extends Authenticatable
{
    // ...

    protected $casts = [
        'password'=> 'hashed',
    ];
}

现在,当我们创建一个新用户时,我们可以如此移除password字段的手动哈希

use App\Models\User;

$user = User::create([
    'name' => 'Ash',
    'email' => '[email protected]',
    'password' => 'password',
]);

值得注意的是,如果值已经被哈希,则hashed转换不会再次对值进行哈希。因此,如果您同时使用Hash::makehashed转换来手动哈希值,您不必担心值会被重复哈希。

我应该使用哪种方法?

手动哈希值的优点是它更加确切和明显,表明该值被哈希。这意味着开发者可以更容易地通过一眼理解代码在做什么。而使用hashed模型转换时,如果没有了解模型转换,则不太明显地知道值正在被哈希。

另一方面,使用hashed模型转换的优点是代码量更少。虽然我认为这个优点很小,因为您并没有真正地移除很多代码。

请注意,转换仅在你使用模型在数据库中创建或更新记录时应用。例如,如果您使用类似DB门面(facade)的某物来创建用户,则他们的密码不会使用hashed转换进行哈希。因此,在这种情况下,您需要记住手动哈希值。

在我看来,我认为两种方法都是完全可以接受的,并且各有优缺点,这完全取决于个人喜好。我只是强烈建议在您的项目中只使用一种方法,以避免任何混淆并保持一致。这将降低有人错误地认为值正在手动哈希而实际上并不是如此的可能性。

测试值是否已被哈希

无论您选择哪种方法,有一个以确保值被正确哈希的测试总是很有用的。

在您可能不小心假设值已被自动哈希但实际上并非如此的情况下,这非常有帮助。

让我们快速看一下您可以编写的基本测试,以确保值在存储在数据库中时被哈希。

我们可以设想我们有一个基本的App\Services\UserService类,我们使用它来创建新用户。该UserService类具有一个接受虚构的NewUserData类的createUser方法。该NewUserData类有一个我们想要确保在存储到数据库之前被哈希的password属性。

UserService类可能看起来像这样

declare(strict_types=1);

namespace App\Services;

use App\DataTransferObjects\User;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

final readonly class UserService
{
    public function createUser(NewUserData $userData): User
    {
        return User::create([
            'name' => $userData->name,
            'email' => $userData->email,
            'password' => $userData->password,
        ]);
    }
}

我们可能想要编写如下的测试

declare(strict_types=1);

namespace Tests\Feature\Services\UserService;

use App\DataObjects\NewUserData;
use App\Models\User;
use App\Services\UserService;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Hash;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

final class CreateUserTest extends TestCase
{
    use LazilyRefreshDatabase;

    #[Test]
    public function user_can_be_stored(): void
    {
        $newUserData = new NewUserData(
            name: 'Ash',
            email: '[email protected]',
            password: 'password',
        );

        $user = (new UserService())->createUser($newUserData);

        // Assert the user was stored in the database.
        $this->assertDatabaseHas(User::class, [
            'id' => $user->id,
            'name' => 'Ash',
            'email' => '[email protected]',
        ]);

        // Assert the password has been hashed correctly.
        $this->assertTrue(
            Hash::check('password', $user->password),
        );
    }
}

在上面的测试中,我们正在调用UserService类的createUser方法,并传递一个包含一些虚拟数据的NewUserData对象。我们断言用户是否已存储在数据库中。

由于我们无法直接使用assertDatabaseHas断言比较密码的哈希值与我们传递的值,我们可以使用Hash::check方法来检查值是否正确地被哈希。

如果值没有被哈希,则Hash::check方法将返回false,测试将失败。这意味着,如果在password字段中意外地删除了哈希,我们将能够发现它。

结论

希望这篇文章能展示您如何自动哈希Laravel模型字段。

如果您喜欢阅读这篇文章,我很乐意听听您的看法。同样,如果您有任何可以改善未来文章的意见,我也很愿意听取。

您也许也对查看我的220多页的电子书感兴趣:“Battle Ready Laravel”,它更深入地涵盖了类似的主题。

或者,您也许想查看我的其他440多页的电子书:“Consuming APIs in Laravel”,它教您如何使用Laravel消费其他服务的API。

继续打造令人惊叹的产品!🚀

最后更新6个月前。

driesvints, ash-jc-allen, jerexbambex点了赞这篇文章

3
喜欢这篇文章吗?告诉作者并给他们一点掌声!
ash-jc-allen (Ash Allen) 我是一名来自英国的普雷斯顿的自由职业Laravel开发者。我维护Ash Allen设计博客,并参与许多酷炫和令人兴奋的项目🚀

您可能还感兴趣的其它文章

2024年3月11日

如何使用Larastan将您的Laravel应用程序从0到9提升

在您的Laravel应用程序执行之前找到错误是可能的,归功于Larastan...

阅读文章
2024年7月19日

无需特质标准化API响应

我发现,多数创建用于API响应的库都是用特质实现的...

阅读文章
2024年7月17日

在您的Laravel项目中通过Discord通知收集反馈

如何在Laravel项目中创建反馈模块,并在接收到消息时收到Discord通知...

阅读文章

我们想感谢这些出色的公司 对我们的支持

您的标志在这里?

Laravel.io

Laravel问题解决、知识共享和社区建设的Laravel门户。

© 2024 Laravel.io - 版权所有。