【问题标题】:Using Laravel's Gate / Authorization in VueJs在 VueJs 中使用 Laravel 的门/授权
【发布时间】:2018-02-02 18:25:20
【问题描述】:

我不确定以前没有处理过这个问题,但是我该如何使用 VueJs 并在 Vue 模板中授权操作?

如果我使用 Laravel 的刀片,这很容易(使用 @can 指令),但是在 Google 上搜索了几个小时之后,没有文档或任何方法可以在 Vue 中执行此操作。

现在,我知道我可以简单地将用户权限加载到视图内的数组/JSON 对象中,但似乎无法使用 Laravel 的门方法在 Vue 模板中显示/隐藏操作以确定是否允许用户对特定记录执行操作。

例如,有一个 cmets 列表,但用户必须拥有评论才能看到“编辑”按钮。

问题是,如果我在 Vue 中实现逻辑,我将为此在整个后端和前端复制授权逻辑。

使用 Laravel 的策略,我能够执行特定操作的复杂授权。但我对如何在 Vue 中实施该政策感到困惑。

还有更复杂的场景,例如如果具有admin 角色的用户正在浏览 cmets,即使他们不拥有评论,他们也应该能够编辑它。

有人对这种场景有什么建议吗?

编辑:

现在我可以为我的模型添加一个属性访问器,例如:

型号:

class Comment extends Model
{
    protected $appends = ['can_update'];

    public function getCanUpdateAttribute()
    {
        return Gate::allows('update', $this);
    }
}

Vue:

<button v-if="comment.can_update">Edit</button>

但这似乎又在重复我的策略中已经存在的逻辑。

【问题讨论】:

  • 这是一个值得思考的好点。
  • 当您从服务器获取记录时,此时您知道某人是否是管理员,他们是否可以编辑/创建/删除等 - 为什么不以简单的形式提供布尔字段。假设您要列出这些虚构的属性。在服务器端,只需添加 is_admincan_edit 字段并将其传递回 vue 应用程序。根据值(真/假),渲染行/按钮/颜色/等。只需使用Vue渲染服务器提供的数据。
  • 查看这篇文章,它在前端使用 Laravel ACL pineco.de/implementing-laravels-authorization-front-end
  • 您可以在 Vue 组件中评估门,因为 Vue 是客户端,并且策略是定义的(并在服务器端进行评估)。如果您在策略方法中执行数据库查询,则无法在客户端执行此操作。您需要在服务器端评估策略方法,并将结果传递给您的视图组件。

标签: laravel laravel-5 vue.js vuejs2 laravel-5.4


【解决方案1】:

在我的一个项目中,权限存储为字符串,即“post.read”、“post.view”。这些权限在登录时传递给前端。

我构建了一个小而简单的插件Vue Quick ACL,它将功能抽象出来并管理更改用户和用户权限时的反应。

// UserResource

return [
 'name' => $this->name,
 'permissions' => $this->permissions
];

然后您可以在前端与用户一起存储这些权限:

// Login.vue

export default {
  methods: {
    login() {
      Api.login()
        then(({data}) => {
          this.$setUserPermissions(data.permissions)
        })
    }
  }
}
// Component.vue

<template>
  <button v-if="$can('post.delete')">Delete</button>
</template>

【讨论】:

    【解决方案2】:

    我最终使用 Laravel resources 来完成这项工作。

    这是一个示例(注意 can 数组键):

    class Ticket extends Resource
    {
        /**
         * The "data" wrapper that should be applied.
         *
         * @var string
         */
        public static $wrap = 'ticket';
    
        /**
         * Transform the resource into an array.
         *
         * @param \Illuminate\Http\Request $request
         *
         * @return array
         */
        public function toArray($request)
        {
            return [
                'id' => $this->id,
                'answer_id' => $this->answer_id,
                'summary' => $this->summary,
                'description' => $this->description,
                'creator' => $this->creator,
                'created_at' => $this->created_at,
                'updated_at' => $this->updated_at,
                'reported_at' => $this->reported_at,
                'closed_at' => $this->closed_at,
                'closed' => $this->closed,
                'answered' => $this->answered,
                'can' => $this->permissions(),
            ];
        }
    
        /**
         * Returns the permissions of the resource.
         *
         * @return array
         */
        protected function permissions()
        {
            return [
                'update' => Gate::allows('update', $this->resource),
                'delete' => Gate::allows('delete', $this->resource),
                'open' => Gate::allows('open', $this->resource),
            ];
        }
    }
    

    这让我可以在 Vue 模板中使用简单的布尔逻辑来控制前端的访问,而不是在前端复制实际的权限逻辑:

    <router-link v-if="ticket.can.update" :to="{name:'tickets.edit', params: {ticketId: ticket.id}}" class="btn btn-sm btn-secondary">
        <i class="fa fa-edit"></i> Edit
    </router-link>
    

    此外,如果用户能够创建资源,我使用 Laravel 资源 collections 来应用权限:

    class TicketCollection extends ResourceCollection
    {
        /**
         * The "data" wrapper that should be applied.
         *
         * @var string
         */
        public static $wrap = 'tickets';
    
        /**
         * Get any additional data that should be returned with the resource array.
         *
         * @param \Illuminate\Http\Request $request
         *
         * @return array
         */
        public function with($request)
        {
            return [
                'can' => [
                    'create' => Gate::allows('create', Ticket::class),
                ],
            ];
        }
    }
    

    然后在我的 API 控制器中:

    public function index()
    {
        $tickets = Ticket::paginate(25);
    
        return new TicketCollection($tickets);
    }
    
    public function show(Ticket $ticket)
    {
        $ticket->load('media');
    
        return new TicketResource($ticket);
    }
    

    这让我可以验证当前经过身份验证的用户是否有权创建列出的资源,因为我们没有要验证的实际资源,我们可以在返回的集合上执行此操作,因为它完全相关。

    在我看来,实施这种模式是管理授权的最简单方法,无需在整个 Vue 应用程序中复制实际授权逻辑,也无需使用刀片将权限单独注入组件。

    如果您的嵌套组件也需要权限,那么将权限注入组件最终会导致我遇到问题,因为您需要将子组件权限传递给父组件才能验证它们。

    对于嵌套权限,您可以从父资源中返回包含 can 权限数组的关系的子资源,因此您可以使用 Vue 轻松循环这些资源,并使用简单的逻辑来确定用户也可以访问这些。

    这种方法也很有益,因此我可以通过服务器端在不经常更改的资源上缓存每个用户的权限。

    【讨论】:

    • 我很喜欢这个方案,很优雅
    • 这太棒了。虽然我认为它会更好地包含在元中。您可以使用 with() 方法添加元数据。 laravel.com/docs/5.8/eloquent-resources#adding-meta-data
    • 使用该方法获取记录列表时有性能命中,能否分享一下您缓存Policy授权的策略?
    • 如果权限包含在资源本身中,并且您想阻止用户甚至阅读票证,那么他们可以检查响应并查看票证内容。
    • @digout 在想要限制查看内容的情况下,您必须根据当前用户权限有条件地添加数据,而不是全部重新调整。我的解决方案只是将权限附加到资源 - 它不限制返回的实际内容。
    【解决方案3】:

    目前,如果不将后端的代码复制到前端,是无法实现的。

    Fullstack Radio(17:15) 的这一集中,Jeffrey Way 和 Adam Wathan 准确地谈到了这一点。他们和我的观点相同,目前,他们的做法和你一样。

    他们还谈到了使用以下道具:

    <post-component :can-update="{{ $user->can('update', $post) }}"></post-component>
    

    我希望这个答案会有所帮助。

    【讨论】:

    • 谢谢伊恩,听好了!感谢您链接那一集。怎么还没解决呢?
    • 是的。我猜 Jeffrey 说过他会为此努力。
    • 这太棒了,我希望很快会有事情发生。同时,我将继续使用您提到的道具,因为目前似乎没有其他选择:(再次感谢您的回答!
    • 查看这篇文章,它在前端使用 Laravel ACL pineco.de/implementing-laravels-authorization-front-end
    • @MuhammadMohsen 不幸的是没有。我最终使用了自己的解决方案,根据用户请求的返回资源返回布尔值。这使我不必在两端重新创建我的授权逻辑(这将是一场噩梦)。有关更多信息,请参阅我的答案。
    猜你喜欢
    • 2019-01-29
    • 2022-11-26
    • 2020-12-18
    • 2019-10-22
    • 2017-08-14
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多