ホームページ  >  記事  >  ウェブフロントエンド  >  Vue2.0でユーザー権限制御を実装する

Vue2.0でユーザー権限制御を実装する

亚连
亚连オリジナル
2018-06-06 14:14:171793ブラウズ

この記事では、主に Vue2.0 のユーザー権限制御ソリューションの例を紹介します。編集者が非常に優れていると考えたので、参考として共有します。エディターをフォローして見てみましょう

Vue-Access-Control は、Vue/Vue-Router/axios に基づいた、ルーティング、ビュー、リクエストの 3 つのレベルを制御するフロントエンドのユーザー権限制御ソリューションです。開発 ユーザーは、任意の粒度のユーザー権限制御を実現できます。

インストール

バージョン要件

  1. Vue 2.0x

  2. Vue-router 3.x

git: git clone https://github. com/tower1 229/ Vue -Access-Control.git

npm: npm i vue-access-control

実行

//开发
npm run dev

//构建
npm build

概要

全体的なアイデア

セッションの開始時に、次のコマンドだけで Vue インスタンスを初期化します。ログインルーティング 、ルートコンポーネントの作成されたフックでログインページへのルートを指示します。ユーザーがログインに成功すると、フロントエンドはユーザートークンを取得し、axios インスタンスを設定して一律に {"Authorization":token} を追加します。リクエストヘッダーを使用してユーザー認証を実装し、現在のユーザーの権限を取得します。データには主にルーティング権限とリソース権限が含まれます。その後、ルートが動的に追加され、メニューが生成され、権限指示とグローバル権限検証メソッドが実装され、リクエストインターセプタが実行されます。この時点で、権限制御の初期化が完了します。ルートを動的にロードした後、ルーティング コンポーネントがロードされてレンダリングされ、フロントエンド インターフェイスが表示されます。

ブラウザの更新ルートのリセットの問題を解決するには、トークンを取得した後、ローカルにトークンがあるかどうかを確認するためにルートコンポーネントの作成されたフックを保存する必要があります。トークンを直接使用して権限を取得し、ログインせずに初期化できます。トークンが有効で、現在のルートにアクセス権がある場合、ルーティング コンポーネントは読み込まれて正しく表示されます。現在のルートにアクセス権がない場合は、ジャンプします。ルーティング設定に従って 404 まで; トークンが無効な場合、バックエンドは 4xx ステータス コードを返し、フロントエンドは axios インスタンスとして統合される必要があります。エラー インターセプターを追加し、4xx ステータス コードが発生したときに終了操作を実行し、 sessionStorage データを取得してログイン ページにジャンプすると、ユーザーは再度ログインできるようになります。

最小依存関係の原則

Vue-Access-Control は、Vue/Vue-Router/axios 以外の依存関係を持たない単一ドメイン ソリューションとして位置付けられており、理論的には、アクセス許可制御要件を持つすべての人に適用できます。 Vue プロジェクトでは、Webpack テンプレートに基づいてプロジェクトが開発および構築され、ほとんどの新しいプロジェクトは、チェックアウトされたコードに基づいて直接開発し続けることができます。プロジェクトで導入された追加の Element-UI と CryptoJS は、デモ インターフェイスの開発にのみ使用されるため、必要なく、プロジェクト アプリケーションで自分で選択できます。

ディレクトリ構造

src/
 |-- api/         //接口文件
 |   |-- index.js       //输出通用axios实例
 |   |-- account.js      //按业务模块组织的接口文件,所有接口都引用./index提供的axios实例
 |-- assets/
 |-- components/
 |-- router/
 |   |-- fullpath.js     //完整路由数据,用于匹配用户的路由权限得到实际路由
 |   `-- index.js      //输出基础路由实例
 |-- views/
 |-- App.vue
 ·-- main.js

データ形式の規則

カスタムを使用する場合、同じ ID とparent_id を持つ 2 つのルートは、次の形式のオブジェクト配列である必要があります。ルーティング データの形式。ルーティング制御の関連実装を変更する必要があります。詳細については、「ルーティング制御」を参照してください。

[
  {
   "id": "1",
   "name": "菜单1",
   "parent_id": null,
   "route": "route1"
  },
  {
   "id": "2",
   "name": "菜单1-1",
   "parent_id": "1",
   "route": "route2"
  }
 ]

リソース許可データは、次の形式のオブジェクト配列である必要があります。各オブジェクトは、RESTful リクエストを表し、パラメーター付きの URL をサポートします。特定の形式の手順については、リクエスト制御

 [
  {
   "id": "2c9180895e172348015e1740805d000d",
   "name": "账号-获取",
   "url": "/accounts",
   "method": "GET"
  },
  {
   "id": "2c9180895e172348015e1740c30f000e",
   "name": "账号-删除",
   "url": "/account/**",
   "method": "DELETE"
  }
]

ルーティング制御

を参照してください。 ルーティング制御には、ルートの動的登録とメニューの動的生成の 2 つの部分が含まれます。

動的登録ルート

最初にインスタンス化されたルートには、ログインと 404 パスのみが含まれると予想されます:

[{
 path: '/login',
 name: 'login',
 component: (resolve) => require(['../views/login.vue'], resolve)
}, {
 path: '/404',
 name: '404',
 component: (resolve) => require(['../views/common/404.vue'], resolve)
}, {
 path: '/',
 name: '首页',
 component: (resolve) => require(['../views/index.vue'], resolve),
 children: [{
  path: '/route1',
  name: '栏目1',
  meta: {
   icon: 'icon-channel1'
  },
  component: (resolve) => require(['../views/view1.vue'], resolve)
 }, {
  path: '/route2',
  name: '栏目2',
  meta: {
   icon: 'ico-channel2'
  },
  component: (resolve) => require(['../views/view2.vue'], resolve),
  children: [{
   path: 'child2-1',
   name: '子栏目2-1',
   meta: {
    
   },
   component: (resolve) => require(['../views/route2-1.vue'], resolve)
  }]
 }]
}, {
 path: '*',
 redirect: '/404'
}]

次に、ホームページとそのサブルートを取得する必要があります。プロジェクト全体の完全なルーティング データを事前にローカルで取得し、ユーザーの権限に基づいて完全なルーティング データをフィルタリングします。

フィルタリングの実装アイデアは、最初にバックエンドから返されたルーティング データを次のハッシュ構造に処理することです:

let hashMenus = {
  "/route1":true,
  "/route1/route1-1":true,
  "/route1/route1-2":true,
  "/route2":true,
  ...
}

次に、ローカルの完全なルートを走査し、ループ内の上記の構造のキー形式にパスを結合します。 、および hashMenus[route] を渡します。具体的な実装については、App.vue ファイルの getRoutes() メソッドを参照してください。

バックエンドから返されたルーティング権限データが契約と異なる場合は、実際に利用可能なルーティング データを取得できる限り、最終的に addRoutes() メソッドを使用して動的に追加することができます。それらをルーティング インスタンスに追加する場合は、404 ページの最後にファジー マッチングを配置する必要があることに注意してください。

ダイナミックメニュー

ルーティングデータはナビゲーションメニューの生成に直接使用できますが、ルーティングデータはルートコンポーネントで取得され、ナビゲーションメニューはindex.vueコンポーネントに存在します。明らかに、メニューデータを共有する必要があります。一般に、最初に思い浮かぶのは Vuex ですが、これは Vuex の最適な使用シナリオではなく、無駄を最小限に抑えるためです。ここでは、ルート コンポーネントの data.menuData にメニュー データをハングし、this.$parent.menuData を使用してそれをホームページ上で取得するのが、最も簡単な方法です。

さらに、ナビゲーション メニューには列アイコンを追加する必要がある場合があります。これは、たとえば、アイコン クラスまたは Unicode をルート メタに保存することで実現できます。テンプレートでアクセスしてアイコン ラベルを生成します。

在多角色系统中可能遇到的一个问题是,不同角色有一个名字相同但功能不同的路由,比如说系统管理员和企业管理员都有”账号管理”这个路由,但他们的操作权限和目标不同,实际上是两个完全不同的界面,而Vue不允许多个路由同名,因此路由的name必须做区分,但把区分后的name显示在前端菜单上会很不美观,为了让不同角色可以享有同一个菜单名称,我们只要将这两个路由的meta.name都设置成”账号管理”,在模板循环时优先使用meta.name就可以了。

菜单的具体实现可以参考views/index.vue。

视图控制

视图控制的目标是根据当前用户权限决定界面元素显示与否,典型场景是对各种操作按钮的显示控制。实现视图控制的本质是实现一个权限验证方法,输入请求权限,输出是否获准。然后配合v-if或jsx或自定义指令就能灵活实现各种视图控制。

全局验证方法

验证方法的的实现本身很简单,无非是根据后端给出的资源权限做判断,重点在于优化方法的输入输出,提升易用性,经过实践总结最终使用的方案是,将权限跟请求同时维护,验证方法接收请求对象数组为参数,返回是否具有权限的布尔值。

请求对象格式:

//获取账户列表
const request = {
 p: ['get,/accounts'],
 r: params => {
  return instance.get(`/accounts`, {params})
 }
}

权限验证方法$_has()的调用格式:

v-if="$_has([request])"

权限验证方法的具体实现见App.vue中Vue.prototype.$_has方法。

将权限验证方法全局混入,就可以在项目中很容易的配合v-if实现元素显示控制,这种方式的优点在于灵活,除了可以校验权限外,还可以在判断表达式中加入运行时状态做更多样性的判断,而且可以充分利用v-if响应数据变化的特点,实现动态视图控制。

具体实现细节参考基于Vue实现后台系统权限控制中的相关章节。

自定义指令

v-if的响应特性是把双刃剑,因为判断表达式在运行过程中会频繁触发,但实际上在一个用户会话周期内其权限并不会发生变化,因此如果只需要校验权限的话,用v-if会产生大量不必要的运算,这种情况只需在视图载入时校验一次即可,可以通过自定义指令实现:

//权限指令
Vue.directive('has', {
 bind: function(el, binding) {
  if (!Vue.prototype.$_has(binding.value)) {
   el.parentNode.removeChild(el);
  }
 }
});

自定义指令内部仍然是调用全局验证方法,但优点在于只会在元素初始化时执行一次,多数情况下都应该使用自定义指令实现视图控制。

请求控制

请求控制是利用axios拦截器实现的,目的是将越权请求在前端拦截掉,原理是在请求拦截器中判断本次请求是否符合用户权限,以决定是否拦截。

普通请求的判断很容易,遍历后端返回的的资源权限格式,直接判断request.method和request.url是否吻合就可以了,对于带参数的url需要使用通配符,这里需要根据项目需求前后端协商一致,约定好通配符格式后,拦截器中要先将带参数的url处理成约定格式,再判断权限,方案中已经实现了以下两种通配符格式:

1. 格式:/resources/:id
   示例:/resources/1
   url: /resources/**
   解释:一个名词后跟一个参数,参数通常表示名词的id  

2. 格式:/store/:id/member
   示例:/store/1/member
   url:/store/*/member

 解释:两个名词之间夹带一个参数,参数通常表示第一个名词的id
对于第一种格式需要注意的是,如果你要发起一个url为"/aaa/bbb"的请求,默认会被处理成"/aaa/**"进行权限校验,如果这里的”bbb”并不是参数而是url的一部分,那么你需要将url改成"/aaa/bbb/",在最后加一个”/“表示该url不需要转化格式。

拦截器的具体实现见App.vue中的setInterceptor()方法。

如果你的项目还需要其他的通配符格式,只需要在拦截器中实现对应的检测和转化方法就可以了。

演示及说明

演示说明:

DEMO项目中演示了动态菜单、动态路由、按钮权限、请求拦截。

演示项目后端由rap2生成mock数据,登录请求通常应该是POST方式,但因为rap2的编程模式无法获取到非GET的请求参数,因此只能用GET方式登录,实际项目中不建议仿效;

另外登录后获取权限的接口本来不需要携带额外参数,后端可以根据请求头携带的token信息实现用户鉴权,但因为rap2的编程模式获取不到headers数据,因此只能增加一个”Authorization”参数用于生成模拟数据。

测试账号:

1. ユーザー名: root
パスワード: 任意
2. ユーザー名: client
パスワード: 任意

以上は、皆さんの参考になれば幸いです。

関連記事:

angularJS でラジオを介して 2 つのオプションの 1 つを使用する方法 (詳細なチュートリアル)

vue での cli の包括的な解釈 (詳細なチュートリアル)

js ステータスを介してラジオを動的に変更する方法(詳細なチュートリアル)

以上がVue2.0でユーザー権限制御を実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。