关闭

在 VueJS应用中管理用户权限

时间: 2018-01-26阅读: 1728标签: vue

在需要身份验证的前端应用里,我们经常想通过用户角色来决定哪些内容可见。比如,游客身份可以阅读文章,但注册用户或管理员才能看到编辑按钮。

前端中管理权限可能会有点麻烦。你之前可能写过这样的代码

if (user.type === ADMIN || user.auth && post.owner === user.id ) {
  ...
}

作为代替方案,一个简洁轻量的库——CASL——可以让管理用户权限变得非常简单。只要你用CASL定义了权限,并设置了当前用户,就可以把上面的代码改为这样:

if (abilities.can('update', 'Post')) {
  ...
}

在这篇文章里,我会展示如何在前端应用里使用vue.js和CASL来管理权限。


注:你没有使用过CASL也可以继续阅读


CASL 速成课程

CASL可以让你定义一系列规则来限制哪些资源对用户可见。

比如,CASL规则能够标明用户可以对给定的资源和实例(帖子、文章、评论等)进行哪些CRUD(Create, Read, Update和Delete)操作。

假设我们有分类广告网站。最显而易见的规则就是:

  • 游客可以浏览所有帖子
  • 管理员可以浏览所有帖子,并且可以更新或删除

使用CASL,我们用AbilityBuilder来定义规则。调用can来定义一条新规则。例如:

const { AbilityBuilder } = require('casl');

export function(type) {
  AbilityBuilder.define(can => {
    switch(type) {
      case 'guest':
        can('read', 'Post');
        break;
      case 'admin':
        can('read', 'Post');
        can(['update', 'delete'], 'Post');
        break;
      // Add more roles here
    }
  }
};

现在,就可以用定义的规则来检查应用权限了。

import defineAbilitiesFor from './abilities';

let currentUser = {
  id: 999,
  name: "Julie"
  type: "registered",
};

let abilities = defineAbilitiesFor(currentUser.type);

vue.component({
  template: `<div><div>
             <div>Please log in</div>
            `,
  props: [ 'post' ],
  computed: {
    showPost() {
      return abilities.can('read', 'Post');
    }
  }
});

查阅官方文档,了解更多CASL详情。


Demo 课程

作为演示,我做了一个用来展示分类广告帖子的服务器/客户端应用。这个应用的规则是:用户能够阅读帖子或发帖,但是只能更新或删除自己的帖子。

我用Vue.js和CASL来方便地运行和扩展这些规则,即使以后添加新的操作或实例也将很方便。

现在我就带你一步步搭建这个应用。如果你想一睹为快,请戳这个Github repo


定义用户权限

我们在 resources/ability.js中定义用户权限。CASL的一个优点是与环境无关,也就是说它既能在Node中运行,也能在浏览器中运行。

我们会把权限定义写到一个CommonJS模块里来保证Node的兼容性(webpack能让这个模块用在客户端)。

resources/ability.js

const casl = require('casl');

module.exports = function defineAbilitiesFor(user) {
  return casl.AbilityBuilder.define(
    { subjectName: item => item.type }, 
    can => {
      can(['read', 'create'], 'Post');
      can(['update', 'delete'], 'Post', { user: user });
    }
  );
};

下面我们来剖析这段代码。

define方法的第二个参数,我们通过调用can来定义了权限规则。这个方法的第一个参数是你要允许的CRUD操作,第二个是资源或实例,在这个例子中是Post。

注意第二个can的调用,我们传了一个对象作为第三个参数。这个对象是用来测试user属性是否匹配我们提供的user对象。如果我们不这么做,那不光创建者可以删帖,谁都可以随便删了。

resources/ability.js

...
casl.AbilityBuilder.define(
  ...
  can => {
    can(['read', 'create'], 'Post');
    can(['update', 'delete'], 'Post', { user: user });
  }
);

CASL检查实例来分配权限时,需要知道实例的type。一种解决方式是把具有subjectName方法的对象,作为define方法的第一个参数,subjectName方法会返回实例的类型。

我们通过在实例中返回type来达成目的。我们需要保证,在定义Post对象时,这个属性是存在的。

resources/ability.js

...
casl.AbilityBuilder.define(
  { subjectName: item => item.type }, 
  ...
);

最后,我们把我们的权限定义封装到一个函数里,这样我们就可以在需要测试权限的时候直接传进一个user对象。在下面的函数中会更易理解。

resources/ability.js

const casl = require('casl');

module.exports = function defineAbilitiesFor(user) {
  ...
};


Vue 中的访问权限规则

现在我们想在前端应用中检查一个对象中,用户具有哪些CRUD权限。我们需要在Vue组件中访问CASL规则。这是方法:

  1. 引入Vue和 abilities plugin。这个插件会把CASL加到Vue的原型上,这样我们就能在组件内调用了。
  2. 在Vue 应用内引入我们的规则(例: resources/abilities.js)。
  3. 定义当前用户。实战中,我们是通过服务器来获取用户数据的,在这个例子中,我们简单地硬编码到到项目里。
  4. 牢记,abilities模块export一个函数,我们把它称为defineAbilitiesFor。我们会向这个函数传入用户对象。现在,无论何时,我们可以通过检测一个对象来得出当前用户拥有何种权限。
  5. 添加abilities插件,这样我们就可以在组件中像这样来进行测试了:this.$can(...)。

src/main.js

import Vue from 'vue';
import abilitiesPlugin from './ability-plugin';

const defineAbilitiesFor = require('../resources/ability');
let user = { id: 1, name: 'George' };
let ability = defineAbilitiesFor(user.id);
Vue.use(abilitiesPlugin, ability);


Post 实例

我们的应用会使用分类广告的帖子。这些表述帖子的对象会从数据库中检索,然后被服务器传给前端。比如:

我们的Post实例中有两个属性是必须的:

  1. type属性。CASL会使用 abilities.js中的subjectName回调来检查正在测试的是哪种实例。
  2. user属性。这是发帖者。记住,用户只能更新和删除他们发布的帖子。在 main.js中我们通过defineAbilitiesFor(user.id)已经告诉了CASL当前用户是谁。CASL要做的就是检查用户的ID和user属性是否匹配。
let posts = [
  {
    type: 'Post',
    user: 1,
    content: '1 used cat, good condition'
  },
  {
    type: 'Post',
    user: 2,
    content: 'Second-hand bathroom wallpaper'
  }
];

这两个post对象中,ID为1的George,拥有第一个帖子的更新删除权限,但没有第二个的。


在对象中测试用户权限

帖子通过Post组件在应用中展示。先看一下代码,下面我会讲解:

src/components/Post.vue

<template>
  <div>
    <div>

      <br /><small>posted by </small>
    </div>
    <button @click="del">Delete</button>
  </div>
</template>
<script>
  import axios from 'axios';

  export default {
    props: ['post', 'username'],
    methods: {
      del() {
        if (this.$can('delete', this.post)) {
          ...
        } else {
          this.$emit('err', 'Only the owner of a post can delete it!');
        }
      }
    }
  }
</script>
<style lang="scss">...</style>

点击Delete按钮,捕获到点击事件,会调用del处理函数。

我们通过this.$can('delete', post)来使用CASL检查当前用户是否具有操作权限。如果有权限,就进一步操作,如果没有,就给出错误提示“只有发布者可以删除!”


服务器端测试

在真实项目里,用户在前端删除后,我们会通过 Ajax发送删除指令到接口,比如:

src/components/Post.vue

if (this.$can('delete', post)) {
  axios.get(`/delete/${post.id}`, ).then(res => {
    ...  
  });
}

服务器不应信任客户端的CRUD操作,那我们把CASL测试逻辑放到服务器:

server.js

app.get("/delete/:id", (req, res) => {
  let postId = parseInt(req.params.id);
  let post = posts.find(post => post.id === postId);
  if (ability.can('delete', post)) {
    posts = posts.filter(cur => cur !== post);
    res.json({ success: true });
  } else {
    res.json({ success: false });
  }
});

CASL是同构(isomorphic)的,服务器上的ability对象就可以从abilities.js中引入,这样我们就不必复制任何代码了!


封装

此时,在简单的Vue应用里,我们就有非常好的方式管理用户权限了。

我认为this.$can('delete', post) 比下面这样优雅得多:

if (user.id === post.user && post.type === 'Post') {
  ...
}

This is not only more difficult to read, but also, there’s an implicit rule here i.e. that a post can be deleted by a user. This rule will undoubtedly be used elsewhere in our app, and should really be abstracted. This is what CASL can do for us.

这不光可读性差,还暗含规则,即用户可以删除帖子。这条规则无疑可以在应用的其它地方使用,应该被抽离出来。这就是CASL为我们所做的。

感谢CASL的作者Sergii Stotskyi对本文的帮助。
翻译来源:众成翻译
原文出处 Managing User Permissions in a VueJS App


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/395

关闭

如何理解Vue.js中的ref及$ref,$

首先我们来看一下vue官方是怎么解释的m好了,我知道很多刚接触的vue的小白其实都不太看的懂官方文档在讲什么,下面我用自己的话翻译一下。

Vue组件的三种调用方式

主要讲Vue三种方式调用组件:v-model或者.sync显式控制组件显示隐藏,通过js代码调用,通过Vue指令调用,在写组件的时候很多写法、灵感来自于element-ui

基于vue移动端UI框架有哪些?vue移动端UI框架总汇

vue现在使用的人越来越多了,这篇文章主要整理一些比较优秀的移动端ui框架,推荐给大家,例如:mint UI、vux、vonic、vant、cube-ui、Muse-ui、Vue-Carbon、YDUI等

vue 自定义指令

接下来我们来看一下钩子函数的参数 (即 el、binding、vnode 和 oldVnode)。但有一些是没有相对应的指令进行操作。在这里以progress(h5的新标签进度条)为例,向大家介绍Vue的一个用于指令扩展的方法:directive(自定义指令)。

Vue项目使用CDN优化首屏加载

将引用的外部js、css文件剥离开来,不编译到vendor.js中,而是用资源的形式引用,这样浏览器可以使用多个线程异步将vendor.js、外部的js等加载下来,达到加速首开的目的。外部的库文件,可以使用CDN资源,或者别的服务器资源等。

Vue2 中三种逻辑复用方法

Mixin对象能够使用组件的任何属性(data,mounted,created,update等),而且当组件使用mixin时,这个mixin的所有信息也会混合到这个组件里

谈谈vue的render函数?

使用vue 这么长时间,对vue 对源码了解还是不多,作为一个不甘平庸的前端小白,决定奋起,那么今天我们就来谈谈vue 的render 函数;打开源码,我发现render 函数返回一个VNode; 可是我们并未在模版中写render 呀,这又是一个什么样的过程呢?

简单理解vue中的$nextTick

Vue.nectTick() 是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的DOM(dom的改变是发生在nextTick()之后),这个方法作用是当数据被修改后使用这个方法

Vue高版本中一些新特性的使用

Vue高版本中一些新特性的使用:深度作用选择器、组件配置项inheritAttrs、组件实例属性$attrs和$listeners、组件选项 provide/inject、作用域插槽 slot-scope、Vue的错误捕获

vue与后台交互ajax数据

Vue.js是一套构建用户界面的渐进式的前端框架。 vueJS与后台交互数据的方法我所了解的有以下几种

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!