在VueJs中使用Laravel的Gate / Authorization

时间:2017-08-24 18:46:53

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

我不确定之前是如何解决这个问题的,但我如何在Vue模板中使用VueJ并授权操作?

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

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

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

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

使用Laravel的政策,我能够执行特定操作的复杂授权。但我很难知道如何在Vue中实施该政策。

还有更复杂的方案,例如,如果具有admin角色的用户正在浏览评论,即使他们不拥有评论,他们也应该能够对其进行编辑。

有没有人对此类情景有任何建议?

修改

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

型号:

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>

但这似乎再次复制了我的政策中已经存在的逻辑。

3 个答案:

答案 0 :(得分:12)

我最终使用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资源集合来应用权限:

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轻松遍历这些资源,并使用简单的逻辑来确定用户也可以访问这些内容。

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

答案 1 :(得分:6)

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

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

他们还谈到了使用道具,如:

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

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

答案 2 :(得分: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>