Skip to content

#[Locked]

#[Locked] 属性防止属性在客户端被修改,保护敏感数据(如模型 ID)免受用户篡改。

基本用法

#[Locked] 属性应用于任何不应从前端更改的公共属性:

php
<?php // resources/views/components/post/⚡show.blade.php

use Livewire\Attributes\Locked;
use Livewire\Component;
use App\Models\Post;

new class extends Component {
    #[Locked] // [tl! highlight]
    public $postId;

    public function mount($id)
    {
        $this->postId = $id;
    }

    public function delete()
    {
        Post::find($this->postId)->delete();

        return redirect('/posts');
    }
};

如果用户尝试通过浏览器 DevTools 或篡改请求来修改锁定的属性,Livewire 将抛出异常并阻止操作执行。

后端修改仍然允许

带有 #[Locked] 属性的属性仍然可以在组件的 PHP 代码中更改。锁定只防止客户端篡改。注意不要在自己的方法中将不受信任的用户输入传递给锁定的属性。

何时使用

在需要以下情况时使用 #[Locked]

  • 存储不应被用户更改的模型 ID
  • 在组件生命周期中保留授权敏感数据
  • 保护任何充当安全边界的公共属性

模型属性默认是安全的

如果你将 Eloquent 模型存储在公共属性中,Livewire 会自动确保 ID 不会被篡改——不需要 #[Locked] 属性:

php
<?php // resources/views/components/post/⚡show.blade.php

use Livewire\Component;
use App\Models\Post;

new class extends Component {
    public Post $post; // Already protected [tl! highlight]

    public function mount($id)
    {
        $this->post = Post::find($id);
    }
};

为什么不用 protected 属性?

你可能想知道为什么不能只使用 protected 属性来保存敏感数据。

记住,Livewire 只在请求之间持久化公共属性。受保护的属性对于静态的、硬编码的值可以正常工作,但任何需要在运行时存储的数据必须使用公共属性才能在请求之间正确持久化。

这就是 #[Locked] 变得必不可少的地方:它为你提供公共属性的持久性,同时保护免受客户端篡改。

Livewire 不能自动做到这一点吗?

在理想的世界中,Livewire 会默认锁定属性,只在该属性使用 wire:model 时允许修改。

不幸的是,这需要 Livewire 解析你所有的 Blade 模板来理解属性是否被 wire:model 或类似 API 修改。

这不仅会增加技术和性能开销,而且如果属性被 Alpine 或任何其他自定义 JavaScript 修改,也不可能检测到。

因此,Livewire 将继续默认使公共属性可自由修改,并为开发人员提供根据需要锁定它们的工具。