RBAC 権限の設計アイデア
目標
RBAC の権限モデルを理解する
バックグラウンド
この目標を達成するために不同的帐号登陆系统后能看到不同的页面,能执行不同的功能
、役割ベースの権限割り当てソリューションである RBAC (Role-Based Access control) 権限モデルという多くのソリューションがあります。
その許可モードは次のとおりです。
3 つの重要なポイント:
ユーザー: システムを使用する人
権限ポイント: このシステムに機能がいくつあるか (例: ページが 3 つあり、各ページで異なる操作が行われます)
役割: さまざまな権限ポイントのコレクション
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-CxmEMg8V-1673401451130)(asset/image-20210427155035187.png)]
- ユーザーに役割を割り当てる
- ロールに権限を割り当てる
実際のビジネスでは:
-
まず従業員に特定の役割を割り当てます
-
次に、特定の権限ポイントをロール (給与ページの給与ページの下にある操作ボタン) に割り当てます。
従業員には権限ポイントがある
従業員の役割の割り当て - ポップアップ レイヤー コンポーネント
バックグラウンド
現時点ではすでにいくつかの役割がシステム内に存在していますが、次にこれらの役割を別の従業員に割り当て、システムに入った後に異なる作業ができるようにします。
ユーザーとロールの関係は ** 1对多
**: ユーザーは複数のロールを持つことができるため、これらの複数のロールの権限を指定します。たとえば、会社の会長は財務監督者とセキュリティキャプテンの役割を持つことができます。会長は財務諸表を読むことができ、モニタリングをチェックすることもできます。
目標
従業員管理ページで[役割の割り当て]をクリックすると、コンポーネントがポップアップ レイヤーの形式で開閉されます。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-kH47ePb8-1673401451131)(asset/permissionUse/18.png)]
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-aQ1JfSXk-1673401451131)(asset/permissionUse/03.png)]
一連の考え
-
特定の機能を分割する(ロールの機能はより複雑になるため、コンポーネントを分割すると作業負荷が軽減されます)
-
ポップアップレイヤーを介して表示を制御します
新しい役割管理コンポーネント
ファイル ** employees/assignRole.vue
** を作成します。テンプレートの内容は次のとおりです
<template>
<!-- // 分配角色 -->
<div>
这里将来会放置多选列表
<div style="margin-top: 20px; text-align: right">
<el-button type="primary">确定</el-button>
<el-button @click="closeDialog">取消</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
roleIds: []
}
},
methods: {
closeDialog() {
}
}
}
</script>
コンポーネントを登録して使用する
従業員管理ホームページのemployee.vueに、上記で追加したコンポーネントを導入します。
import AssignRole from './assignRole'
components: {
// 省略其他....
AssignRole // 注册组件
},
// 使用
<el-dialog :visiable.sync="showDialogRole">
<assign-role/>
</el-dialog>
補足データ項目は、ポップアップ レイヤーの表示と非表示を制御します。
data () {
return {
// 省略其它
showDialogRole: false
}
}
従業員の役割の割り当て - 基本的なやり取り
目標
ポップアップレイヤーを閉じたエフェクトの表示が完了しました
インタラクション効果 - ポップアップレイヤーを表示
[役割の割り当て]ボタンをクリックし、ID を記録し、箇条書きレイヤーを表示します。
レンプレート
<el-button type="text" size="small" @click="hAssignRole(scope.row)">分配角色</el-button>
コード
hEditRole({
id}) {
console.log('当前要分配角色id是', id)
this.showRoleDialog = true
}
インタラクション効果 - ポップアップレイヤーを閉じる
次の操作を行うとポップアップが閉じます。
- ユーザーがキャンセルボタンをクリックした
- ユーザーが「OK」ボタンをクリックすると、操作は成功しました。
- ユーザーがポップアップの右上隅にある [X] をクリックします。
<el-dialog
title="分配角色"
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="showDialogRole"
>
<assign-role @close="showDialogRole=false" />
</el-dialog>
サブアセンブリ
<template>
<div>
<el-checkbox-group v-model="roleIds">
<el-checkbox label="110">管理员</el-checkbox>
<el-checkbox label="113">开发者</el-checkbox>
<el-checkbox label="115">人事</el-checkbox>
</el-checkbox-group>
<div style="margin-top: 20px; text-align: right">
<el-button type="primary">确定</el-button>
+ <el-button @click="closeDialog">取消</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
roleIds: []
}
},
methods: {
closeDialog() {
+ this.$emit('close')
}
}
}
</script>
従業員の割り当ての役割 - 役割のリストを取得し、el-checkbox で表示します。
コンポーネント:employees/assignRole.vue
目標
リクエストを送信して、このシステム内のすべてのロールのリストを取得し、el-checkbox-group に表示します。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-SHM57fGx-1673401451132)(asset/07-1619407883065.png)]
一連の考え
- 静的テンプレートを準備し、el-checkbox-groupを学習する
- APIインターフェースを準備する
- リクエストを送信してバックエンド データを取得し、レンダリングします。
el-checkbox-group 複数選択ボックスを学習する
レンプレート
<el-checkbox-group v-model="roleIds">
<el-checkbox label="110">管理员</el-checkbox>
<el-checkbox label="113">开发者</el-checkbox>
<el-checkbox label="115">人事</el-checkbox>
</el-checkbox-group>
複数の選択肢を表すために使用される el-checkbox-group の場合:
- v-model の値は配列です (複数の選択を示します)
- この項目を選択した後の値は、子要素 el-checkbox の label 属性によって決まります。
データ
data () {
return {
roleIds: [] // 保存当前选中的权限列表
}
}
ロールのリストを取得するための API を準備する
目標は、すべてのキャラクターを取得することです。ただし、バックエンドは、すべてのロールを直接取得するための既製のインターフェイスを提供しません。
注: 現在の関数専用のロールのリストはありません。データを取得するために、一時的に pageSize を 100 (最初のページ、1 ページあたり 100 項目を取得することに相当) として使用できます。ファイル内でsrc\api\setting.js
、
/**
* 获取所有角色信息
* @param {*} params {page, pagesize}
* @returns
*/
export function getRoles(params) {
return request({
url: '/sys/role',
method: 'GET',
params: params
})
}
ビジネスコンポーネントで呼び出される
src\views\employees\assignRole.vue
_で
<script>
import {
getRoles } from '@/api/setting'
export default {
data() {
return {
roleIds: [],
+ list: []
}
},
created() {
this.loadRoles()
},
methods: {
async loadRoles() {
const {
data } = await getRoles({
page: 1, pagesize: 100 })
+ this.list = data.rows
},
closeDialog() {
this.$emit('close')
}
}
}
</script>
テンプレート内のデータをレンダリングする
<el-checkbox-group v-model="roleIds">
<!-- 注意:label决定当前选中的值 -->
<el-checkbox v-for="item in list" :key="item.id" :label="item.id">
{
{ item.name }}
</el-checkbox>
</el-checkbox-group>
注: ラベルは現在選択されている値を決定します
まとめ
従業員の割り当てロール - データのフェッチとバックフィル
目標
現在のユーザーがすでにいくつかのロール データを設定している場合は、設定されたロール データが最初に表示されます。いくつかのチェックボックスが選択されています。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-YLTZ10fZ-1673401451132)(asset/08-1619407893456.png)]
一連の考え
親コンポーネントでユーザー ID を渡します
ポップアップレイヤーを開いた後、ユーザーIDに基づいて現在のロール情報を取得し、エコーします。
父から息子へ - 父
データ項目を定義する
data() {
return {
// 省略其他 ...
curEmployeId: '', // 当前的员工编号
}
}
役割の割り当てをクリックしたら、値を割り当てます
// 用户点击分配角色
hAssignRole(id) {
this.showDialogRole = true
this.curEmployeId = id
}
レンプレート
小道具を子コンポーネントに渡す
<el-dialog :visible.sync="showDialogRole" title="权限">
<assign-role
+ :employee-id="curEmployeId"
@close="showDialogRole=false"
/>
</el-dialog>
父から息子へ - 受け取る息子
<script>
import {
getUserDetailById } from '@/api/user'
export default {
props: {
// 用户的id 用来查询当前用户的角色信息
employeeId: {
type: String,
required: true
}
},
created() {
this.loadRoles()
},
methods: {
async loadRoles() {
const res = await getRoles({
page: 1, pagesize: 100 })
// 保存所有的角色
this.list = res.data.rows
// console.log('loadRoles...........', res)
const info = await getUserDetailById(this.employeeId)
console.log('getUserDetailById...........', info)
// 保存这个员工当前的已经有的角色
this.roleIds = info.data.roleIds
},
}
</script>
従業員の割り当てロールのバックフィルの問題: 作成されたものは 1 回だけ実行される
理由
サブコンポーネントはダイアログ内にネストされているため、作成されるのは 1 回だけです。create は 1 回だけ実行され、その後の表示操作や非表示操作によってコンポーネントが再構築されることはありません。そのため、後で開いたコンテンツは最初と同じです。 。
解決
解決策 1: ポップアップ レイヤーが非表示になっている場合は、サブコンポーネントを破棄します。
<el-dialog
title="分配角色"
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="showDialogRole"
>
<assign-role
+ v-if="showDialogRole"
:employee-id="curEmployeId"
@close="showDialogRole=false"
/>
</el-dialog>
長所: シンプル; 短所: コンポーネントを破棄するとパフォーマンス上の問題が発生する
オプション II:
アイデア: クリックして親コンポーネントでロールを割り当てると、子コンポーネントのメソッドを直接呼び出してデータを取得します。
子コンポーネントへの参照を追加する
<assign-role
ref="assignRole"
:employee-id="curEmployeId"
@close="showDialogRole=false"
/>
// 用户点击分配角色
hAssignRole(id) {
this.showDialogRole = true
this.curEmployeId = id
console.log('父组件', this.curEmployeId)
// this.$nextTick
// 直接找到子组件,调用方法去获取最新的数据
this.$nextTick(() => {
this.$refs.assignRole.loadRoles()
// console.log('子组件中的props', this.$refs.assignRole.employeeId)
})
}
サブグループ価格で作成された削除
// created() {
// // 组件创建时执行一次
// this.loadRoles()
// },
従業員の役割の割り当て - 保存
目標
ユーザーが変更して割り当てられた役割の特定の機能を保存します
一連の考え
カプセル化インターフェース -> 呼び出しインターフェース
役割の割り当てインターフェイス
** api/employees.js
** ファイルに、assignRoles というメソッドを追加します。
/**
* @description: 为用户分配角色
* @param {*} data { id:当前用户id, roleIds:选中的角色id组成的数组 }
* @return {*}
*/
export function assignRoles(data) {
return request({
url: '/sys/user/assignRoles',
data,
method: 'put'
})
}
業務コードへの保存を確認する
上記で定義した API をインポートします
import {
assignRoles } from '@/api/employees'
ボタンにクリックイベントを追加する
<template>
<el-button type="primary" size="small" @click="hSave">确定</el-button>
</template>
追加の保存されたコールバック
// 保存当前角色信息
async hSubmit() {
const res = await assignRoles({
id: this.employeeId, roleIds: this.roleIds })
console.log('保存角色', res)
this.$emit('update-close')
}
親コンポーネントでイベントをリッスンします
<assign-role
ref="assignRole"
:employee-id="curEmployeId"
@update-close="hUpdateClose"
@close="showDialogRole=false"
/>
hUpdateClose:
// 用户分配角色成功
hUpdateClose() {
this.showDialogRole = false
this.loadEmployeeList()
}
役割の割り当て権限 - 概要
役割に権限を割り当てる理由
ユーザーの役割は何ですか、ユーザーには特定の機能があります
前のコードでは、ユーザーにロールが追加されているため、従業員が実行できる内容は、そのロールで実行される特定の機能によって異なります。
パッケージ化コンポーネントを必要とする権利管理機能が多数あります。
役割の割り当て権限 - ポップアップ スペース コンポーネントと基本的な操作
目標
ロール管理モジュール (views/settings/settings.vue) で、サブコンポーネントを実装します。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-0zTwqJw2-1673401451132)(asset/assign Permissions.gif)]
一連の考え
ポップアップの準備 -> イベントの登録 -> データメソッドの提供
役割に権限ポイントを割り当てる作業を完了する
サブコンポーネントのカプセル化
まず、バックアップの設定で assignPermission.vue コンポーネントをカプセル化します。
|--settings
|---------settings.vue # 角色管理主页
|---------assignPermission.vue #给角色分配权限
settings.vue内で参照・使用されます。
親コンポーネントに爆弾レイヤーを追加し、子コンポーネントを導入します
settings.vue にサブコンポーネントを導入する
import assignPermission from './assignPermission'
登録
components: {
assignPermission
},
el-dialog コンポーネントをテンプレートに追加してインポートします。
<!-- 分配权限的弹层 -->
<el-dialog
title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)" :visible.sync="showDialogAssign">
<assign-permission />
</el-dialog>
補足データ
return {
//... 省略其它
showDialogAssign: false, // 分配权限对话框
}
インタラクション - ポップアップレイヤーを表示
ポップアップを表示します。ボタンにクリックイベントを追加する
<el-button size="small" type="success" @click="hAssign">
分配权限
</el-button>
コールバックで showDialogAssign を true に設定します
methods:{
hAssign() {
this.showDialogAssign = true
}
}
インタラクション非表示ポップアップ レイヤー
カスタム イベント: 子から親へ
<el-dialog
title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)"
:visible.sync="showDialogAssign"
>
<assign-permission
+ @close="showDialogAssign=false"
/>
</el-dialog>
子コンポーネント内で
methods: {
hCancel() {
// 通过父组件去关闭弹层
this.$emit('close')
}
}
役割の割り当て権限 - 権限ポイントのデータを取得して表示する
目標
assignPermission.vue コンポーネントでは、現在のシステム内のすべての権限ポイント データが取得され、ツリー構造で表示されます。目的の効果は次のとおりです。
[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-oYbV8FQO-1673401451133)(asset/image-20210428100556359.png)]
一連の考え
-
権限ポイントインターフェイスの準備
-
ポップアップが表示されたら、次のようにします。
- API を呼び出してデータを取得するリクエストを送信します。
- データの形式変換(配列からツリーへ)
- テンプレートバインディング(el-tree上にデータを表示)
APIを準備する
src\api\permission.js に API を準備します (この API は権限ポイントのページで使用されています)
import request from '@/utils/request'
// 获取权限点列表
export function getPermissionList(params) {
return request({
url: '/sys/permission',
params
})
}
データ項目の準備
permissionData: [] // 存储权限数据
データを取得するリクエストを送信する
インポート方法
import {
getPermissionList } from '@/api/permission'
import {
tranListToTreeData } from '@/utils/index'
呼び出されて作成されました
created() {
this.loadPermissionList()
},
async loadPermissionList() {
// 发送请求, 获取权限列表
const { data } = await getPermissionList()
console.log('权限列表的数据是', data)
this.permissionData = tranListToTreeData(data)
}
データをel-treeに表示する
<!-- 权限点数据展示 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
/>
注: 小道具
役割の割り当て権限 - el-tree のプロパティの設定
目標
el-tree のその他の設定:
- 選択ボックスを表示
- デフォルトですべて展開
- 親子関係を閉じる
属性の設定
https://element.eleme.io/#/zh-CN/component/tree
- show-checkbox チェックボックスを表示
- default-expand-all デフォルトで展開します
- check-strictly を true に設定すると、親子の関連付けを閉じることができます
<!-- 权限点数据展示 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
default-expand-all
:show-checkbox="true"
:check-strictly="true"
/>
デフォルトの展開すべての書き込みメソッドは次と同等です。:default-expand-all="true"
効果
役割の割り当て権限 - データのバックフィル
目標
現在のユーザーは、エコーアウトする必要がある既存の権限を持っている可能性があります。
一連の考え
- APIを準備する
- 現在のパラメータを組み立て、API を呼び出してデータを取得します。
- データバックフィルをツリーに表示する
APIを準備する
ファイル: src\api\settings.js に getRoleDetail メソッドを追加します。
/**
* @description: 获取角色详情
* @param {*} id 角色id
* @return {*}
*/
export function getRoleDetail(id) {
return request({
url: `/sys/role/${
id}`
})
}
親から子にIDを渡す
親コンポーネントのsetting.vueで、データ項目を定義します。
data () {
return {
// 省略其他...
roleId: ''
}
}
クリックして権限を割り当てるときに、roleId を保存します。
<el-button size="mini" type="success" @click="hAssign(scope.row.id)">分配权限</el-button>
対応するコールバックは次のとおりです。
hAssign(id) {
// 记下来id
this.roleId = id
this.showDialogAssign = true
},
サブクラスで roleId を受け取る
assignPerimission.vue では、補足定義 props が roleId 値を受け取ります
props: {
roleId: {
type: String,
required: true
}
}
APIを呼び出してデータを取得する
以前にカプセル化された API を導入する
import {
assignPerm,
+ getRoleDetail
} from '@/api/setting'
created() {
// 调用接口,获取所有的权限点数据
this.loadPermissionList()
// 调用接口,获取当前这个角色已经具备的权限
+ this.loadPermissionByRoleId()
},
async loadPermissionByRoleId() {
// 根据roleId获取当前这个角色已经具备的权限
const res = await getRoleDetail(this.roleId)
+ console.log('获取当前角色的已有的权限点数据', res.data.permIds)
// 回填到树上
this.$refs.tree.setCheckedKeys(res.data.permIds)
},
async loadPermissionList() {
const res = await getPermissionList()
console.log('获取所有的权限点数据', res)
// 转成树状结构
this.permissionData = tranListToTreeData(res.data)
},
データを el-tree にバックフィルする
データが取得されました。いくつかのチェック ボックスがオンになるように、それを el ツリーに入力するにはどうすればよいですか?
回答: setCheckedKeys + ノードキー
公式サイト:https://element.eleme.io/#/zh-CN/component/tree#fang-fa
- 属性ノードキーをツリーに追加
<!-- 权限点数据展示 -->
<el-tree
ref="refTree"
:data="permissionData"
:props="{ label: 'name' }"
:default-expand-all="true"
:show-checkbox="true"
:check-strictly="true"
node-key="id"
/>
- setCheckedKeys を呼び出す
// 获取角色现有的权限
async loadRoleDetail() {
const res = await getRoleDetail(this.roleId)
console.log('获取角色现有的权限', res.data.permIds)
// 回填
this.$refs.refTree.setCheckedKeys(res.data.permIds)
},
まとめ
- el-tree コンポーネントで、setCheckedKeys メソッドを通じてデータを el-tree コンポーネントにエコーします。
ロールの割り当て権限 - データ バックフィルの問題: 作成されたものは 1 回しか実行されません
理由
サブコンポーネントはダイアログ内にネストされているため、作成されるのは 1 回だけです。create は 1 回だけ実行され、その後の表示操作や非表示操作によってコンポーネントが再構築されることはありません。そのため、後で開いたコンテンツは最初と同じです。 。
解決
解決策 1: ポップアップ レイヤーが非表示になっている場合は、サブコンポーネントを破棄します。
<el-dialog
title="分配角色"
:visible.sync="showDialogRole"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<子组件
+ v-if="showDialogAssign"
/>
</el-dialog>
メリット:簡単、最新のデータが得られる。
短所: コンポーネントを破壊すると、特定のパフォーマンス上の問題が発生します。
解決策 2: refs を通じてサブコンポーネントを参照し、そのメソッドを直接呼び出してリクエストを送信する
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-JrmAm932-1673401451133)(asset/image-20210428105921353.png)]
// 用户点击了权限分配
hAssign(id) {
// alert(id)
// 1. 保存角色编号
// 它会影响子组件中的props,但是,这个传递的过程是异步的
this.roleId = id
// 2. 弹层
this.showDialogAssign = true
// 3. 手动调用子组件的loadPermissionByRoleId, 去根据最新的roleId获取权限信息
this.$nextTick(() => {
this.$refs.permission.loadPermissionByRoleId()
})
}
}
役割の割り当ての権限 - 設定の保存
目標
権限の割り当て機能を完了する
一連の考え
APIを準備し、保存をクリックしたときに呼び出す
APIを準備する
ファイル src\api\settings.js に、権限を割り当てる API を追加します。
/**
* 给角色分配权限
* @param {*} data {id:角色id, permIds:[] 所有选中的节点的id组成的数组}
* @returns
*/
export function assignPerm(data) {
return request({
url: '/sys/role/assignPrem',
method: 'put',
data
})
}
API を呼び出してアクセス許可を割り当てる - 分析
上記で定義した API を呼び出して、関連するパラメーターを渡すだけです。
ここには 2 つのパラメータがあります。
- 現在のロール ID は何ですか?
在点击分配权限时,可以从表格中获取, 父传子
- 対応する許可リスト ID は何ですか?
通过el-tree组件的getCheckedKeys来获取用户选中的id列表
API を呼び出して権限を割り当てる - 関数の実現
async hSave() {
const permIds = this.$refs.tree.getCheckedKeys()
// console.log('当前选中的节点数组是', permIds)
const res = await assignPerm({
id: this.roleId,
permIds
})
console.log('保存角色的权限点的结果是', res)
// 提示
this.$message.success('保存角色的权限成功')
// 关闭弹层
this.hCancel()
},
hCancel() {
// 通过父组件去关闭弹层
this.$emit('close-dialog')
// 清空当前的选择
this.$refs.tree.setCheckedKeys([])
}
最後に、ポップアップレイヤーが閉じられると、el-treeでユーザーが選択したデータをクリアします
まとめ
- el-tree は現在選択されているノードのキーを取得します: getCheckedKeys
- el-tree コンポーネントの場合、現在の選択をクリアします: this.$refs.tree.setCheckedKeys([])
ユーザーの権限データを知る
これまでに、RBAC 権限設計のアイデアのすべての側面を実装しました。従業員にロールを割り当て、ロールに権限ポイントを割り当てました。従業員は対応する権限ポイントを持ちます。次に、これらの権限ポイントを実際の権限制御に使用できます。 HR プロジェクトでは、権限を制御する場所が 2 つあります。
- 左側のメニューの権限制御 (異なるユーザーがシステムに入ると、表示されるメニューが異なります)
- 操作ボタンの権限制御(ページ上のボタン、人によって権限が異なります)
権限データはどこにありますか
図に示すように、従業員管理で新しい従業員データを作成し、新しい従業員アカウントを使用してログインし (パスワードは 123456)、個人情報インターフェイス (/api/sys/profile) の返されたデータを表示します。以下では、権限が設定されていません。 の戻りステータスでは、役割の下のメニューとポイントが空であり、現時点では従業員に権限がないことがわかります。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-WN9TIr43-1673401451134)(asset/permissionUse/13.png)]
権限データを変更する方法
管理者アカウントでログインし、新しく作成した新入社員に2つのメニュー権限と1つの操作ボタン権限を割り当て、再度社員アカウントにログインして個人情報の閲覧とデータの返却を行う
手順:
-
権限ポイント管理 >従業員管理
导入,导出
下にボタン操作権限ポイントを追加 -
役割管理 > 新しい役割人事ディレクター> 役割への権限の割り当て (従業員管理、インポート、エクスポート)
-
従業員管理 >人事ディレクターの役割を従業員に割り当てる
-
新しい従業員アカウントに再度ログインし、権限データを確認し、data.roles.menus、points 項目を確認します。
権限アプリケーション - 動的に生成された左側のメニュー - 全体の分析
分析する
ログインに成功したら、ナビゲーション ガードに入ります。
- 個人の許可情報を取得する
- アクセス可能な動的ルートを生成する
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-wMfe8si3-1673401451134)(asset/image-20210428122059657.png)]
例
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-L1oHNZ59-1673401451134)(asset/permissionUse/17.png)]
許可アプリケーション - 左側のメニューを動的に生成 - addRoutes メソッド
目標
vue-router オブジェクトのaddRoutes を学習し、それを使用してルーティング構成を動的に追加します
一連の考え
ユーザーがアクセスできるページ (ルーティング設定) は動的である必要があるため、まずルーティング アドレスを動的に追加できる API をマスターする必要があります。
addRoutesの基本的な使用法
フォーマット
router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])
役割: ルーティング構成を動的に追加する
例
// 按钮
<button @click="hAddRoute">addRoute</button>
// 回调
hAddRoute() {
this.$router.addRoutes([{
path: '/abc',
component: () => import('@/views/abc'),
}])
},
効果
ボタンをクリックすると、アドレスの /abc にアクセスできます。
改造コード
-
router/index.jsのルーティング設定の動的ルーティングの部分を削除します。
const createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), // routes: constantRoutes // 合并动态和静态的路由 , ...asyncRoutes - routes: [...constantRoutes, ...asyncRoutes] + routes: [...constantRoutes] })
-
Permission.js で導入され、addRoutes を使用して動的に追加されます
ルーター内に直接静的に書き込まれたダイナミックルーティングテーブルを、
addRoutes
メソッド呼び出しで追加された形式に変換します。
// 引入所有的动态路由表(未经过筛选)
+ import router, {
asyncRoutes } from '@/router'
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
// 开启进度条
NProgress.start()
// 获取本地token 全局getter
const token = store.getters.token
if (token) {
// 有token
if (to.path === '/login') {
next('/')
} else {
if (!store.getters.userId) {
await store.dispatch('user/getUserInfo')
// 改写成动态添加的方式
+ router.addRoutes(asyncRoutes)
}
next()
}
} else {
// 没有token
if (whiteList.includes(to.path)) {
next()
} else {
next('/login')
}
}
// 结束进度条
NProgress.done()
})
受容効果
-
左側のメニューには静的なホームページのみが残ります(後で解決します)
-
ブラウザに特定の動的ルーティング アドレスを手動で入力することは引き続き可能です。これは、ルーティング システムに実際に動的ルーティングを追加したことを証明しています。
許可申請 - 左側のメニューを動的に生成 - メニューの保存場所を書き換え
問題分析
現在のメニュー レンダリングで使用されるデータ (src\layout\components\Sidebar\index.vue):this.$router.options.routes
このデータは固定されており、addRoutes を通じて追加したルーティング テーブルはメモリ内にのみ存在し、変更されません。this.$router.options.routes
addRoutes メソッドを呼び出した直後にルーティング データをメニューに反映させたい場合は、追加のメソッドを考える必要があります。考えてみてください。Vue 開発において、レスポンシブ機能も動的に変更できることを保証できるテクノロジはどれですか? ビュークス!
目標
メニューデータをvuexに保存する
[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-4Q4i5Ywp-1673401451134)(asset/image-20210428122540218.png)]
vuex管理メニューデータの定義
- 補足モジュール。以下の
src/store/modules
menu.js モジュールを追加します。- データメニューリストの定義
- データsetMenuListを変更するメソッド
// 导入静态路由
import {
constantRoutes } from '@/router'
export default {
namespaced: true,
state: {
// 先以静态路由作为菜单数据的初始值
menuList: [...constantRoutes]
},
mutations: {
setMenuList(state, asyncRoutes) {
// 将动态路由和静态路由组合起来
state.menuList = [...constantRoutes, ...asyncRoutes]
}
}
}
もちろん、このモジュールを src/store/index.js に登録します
+ import menu from './modules/menu'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
settings,
user,
+ menu
},
getters
})
2. setMenuList を送信して完全なメニュー データを生成します
src/permission.js のコードを変更します。
if (!store.getters.userId) {
await store.dispatch('user/getUserInfo')
// 动态添加可以访问的路由设置
router.addRoutes(asyncRoutes)
// 根据用户实际能访问几个页面来决定从整体8个路由设置
// 中,过滤中出来几个,然后保存到vuex中
store.commit('menu/setMenuList', asyncRoutes)
}
3. メニュー生成部分をvuexのデータを利用するように書き換える
src\layout\components\Sidebar\index.vue ファイルで、次の内容を変更します。
routes() {
// 拿到的是一个完整的包含了静态路由和动态路由的数据结构
// return this.$router.options.routes
return this.$store.state.menu.menuList
}
まとめ
[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-FQFaCeh7-1673401451135)(asset/image-20210525124226388.png)]
権限アプリケーション - フィルタリングに権限データを使用します
目標
前のステップで次のことを達成しました。
- 動的ルーティングは、addRoutes を通じてルーティング システムに動的に追加されます。
- 動的ルートをvuexのメニューに保存します
ただし、権限データは一致しませんでした。次に、インターフェイスから返された権限データを通じて動的メニューをフィルタリングし、完成したメニューがユーザー権限に関連していることを確認します。
アイデアをフィルタリングする
[外部リンク画像の転送に失敗しました。ソース サイトには盗難防止リンク メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-JQ1M2Yrk-1673401451135)(asset/image-20210525000107003.png)]
フィルタリングでは名前を識別子として使用し、ルート名が添え字と一致するかどうかを確認します。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-c8GBacDW-1673401451135)(asset/image-20210428153438963.png)]
バックエンドのインターフェイス規約は次のとおりです。
- ページ名: 従業員 ID: 従業員
- ページ名: 権限 ID: 権限
- ページ名: 組織構造識別子: 部門
- ページ名:設定ID:設定
- ページ名: 給与 ID: 給与
- ページ名:承認ID:approvals
- ページ名: 出席 ロゴ: 出席
- ページ名: 社会保障 ロゴ: social_securitys
アクションからメニュー項目を返す
ユーザーがアクセスできるページはアクションを通じて取得され、アクションから戻るだけで済みます。
store/modules/user.js
return ステートメントを変更および補足します。
// 用来获取用户信息的action
async getUserInfo(context) {
// 1. ajax获取基本信息,包含用户id
const rs = await getUserInfoApi()
console.log('用来获取用户信息的,', rs)
// 2. 根据用户id(rs.data.userId)再发请求,获取详情(包含头像)
const info = await getUserDetailById(rs.data.userId)
console.log('获取详情', info.data)
// 把上边获取的两份合并在一起,保存到vuex中
context.commit('setUserInfo', {
...info.data, ...rs.data })
+ return rs.data.roles.menus
},
Permission.jsのactionの戻り値を取得してフィルターする
src/permission.js
_で
if (!store.getters.userId) {
// 有token,要去的不是login,就直接放行
// 进一步获取用户信息
// 发ajax---派发action来做
const menus = await store.dispatch('user/getUserInfo')
console.log('当前用户能访问的页面', menus)
console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
// 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0].name
return menus.includes(routeName)
})
// 一定要在进入主页之前去获取用户信息
// addRoutes用来动态添加路由配置
// 只有在这里设置了补充了路由配置,才可能去访问页面
// 它们不会出现左侧
router.addRoutes(filterRoutes)
// 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
// 生成左侧菜单时,也应该去vuex中拿
store.commit('menu/setMenuList', filterRoutes)
}
効果
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-EgzHy9V4-1673401451135)(asset/permissionUse/15.png)]
まとめ
- アクションからの戻り値を取得する
asyncRoutes.filter
ページ更新時のバグ修正
質問
ブラウザを更新すると、404 ページにジャンプしたことがわかります。
addRouteで追加したルートは更新時に画面が白くなる
理由
ルーティング設定の 404 ページは、すべてのルートの最後ではなく中央にあります。
解決
404 ページをルーティング設定の最後に変更するだけです
コード
-
Routes/index.js の静的ルートから
path:'*'
この項目を削除します -
Permission.js の最後に追加
// if(没有userInfo) {
if (!store.getters.userId) {
// 有token,要去的不是login,就直接放行
// 进一步获取用户信息
// 发ajax---派发action来做
const menus = await store.dispatch('user/getUserInfo')
console.log('当前用户能访问的页面', menus)
console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
// 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0].name
return menus.includes(routeName)
})
// 一定要在进入主页之前去获取用户信息
// 把404加到最后一条
filterRoutes.push( // 404 page must be placed at the end !!!
{
path: '*', redirect: '/404', hidden: true })
// addRoutes用来动态添加路由配置
// 只有在这里设置了补充了路由配置,才可能去访问页面
// 它们不会出现左侧
router.addRoutes(filterRoutes)
// 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
// 生成左侧菜单时,也应该去vuex中拿
store.commit('menu/setMenuList', filterRoutes)
// 解决刷新出现的白屏bug
next({
...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
replace: true // 重进一次, 不保留重复历史
})
} else {
next()
}
ログアウト時にルートをリセットする
質問
終了後、再度ログインすると、メニューが異常であることがわかります (ルートが繰り返されるという出力がコンソールに表示されます)。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-xfeMibRM-1673401451136)(asset/image-20210525161618731.png)]
理由
でルーティング設定がrouter.addRoutes(filterRoutes)
追加され、ログアウトしてもクリアされず、再度ログインして追加し直したので重複してしまいました。
今後、ルーティング権限をリセット(デフォルトに戻す)し、ログイン後に再度追加する必要があります。そうしないと、繰り返し追加されてしまいます。
解決
**router/index.js
ファイル、リセット ルート メソッドが見つかりました
// 重置路由
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
このメソッドはルートを再インスタンス化するもので、新しいルートを変更するのと同じです。以前の ** は加的路由
もう存在しません。ログアウト時に呼び出す必要があります**
store/modules/user.js
import {
resetRouter } from '@/router'
// 退出的action操作
logout(context) {
// 1. 移除vuex个人信息
context.commit('removeUserInfo')
// 2. 移除token信息
context.commit('removeToken')
// 3. 重置路由
resetRouter()
// 4. 重置 vuex 中的路由信息 只保留每个用户都一样的静态路由数据
// 在moudules中的一个module中去调用另一个modules中的mutation要加{root:true}
// context.commit('setMenuList', [], { root: true })
}
許可申請 - ボタンレベル制御 - 分析
目標
従業員 A と従業員 B はどちらも同じページにアクセスできます (従業員管理を例にします)。ただし、従業員 A は Excel をエクスポートできますが、従業員 B は Excel をエクスポートできません。
一連の考え
ユーザーが正常にログインすると、ユーザーがアクセスできるボタンレベルの権限が Points 配列に保存されます。このデータは vuex に保存されるため、プロジェクト内のどこからでもアクセスできます。
- ボタンのロゴがポイントで表示される場合は表示可能
許可申請 - ボタンレベル制御 - カスタム指示
ディレクティブ: v-for、v-if…
ユーザー定義命令: 自己定義命令。命令自体では十分ではないため、自分で定義する必要があります。
ボタンレベルの権限制御に使用します
カスタム ディレクティブを確認する
登録フォーマット
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时inserted会自动执行
inserted: function(el, binding) {
// v-focus="'abc'" ===> binding.value = 'abc'
console.log('focus.... binding', binding.value)
// 聚焦元素
el.focus()
}
})
フォーマットを使用する
<input v-foucs="'xxxx'" />
ボタンレベルで権限検証を解決する
main.js でグローバル ディレクティブを定義します。
// 注册一个全局自定义指令 `v-allow`
Vue.directive('allow', {
inserted: function(el, binding) {
// 从vuex中取出points,
const points = store.state.user.userInfo.roles.points
// 如果points有binding.value则显示
if (points.includes(binding.value)) {
// console.log('判断这个元素是否会显示', el, binding.value)
} else {
el.parentNode.removeChild(el)
// el.style.display = 'none'
}
}
})
使用
<el-button
+ v-allow="'import_employee'"
type="warning"
size="small"
@click="$router.push('/import')"
>导入excel</el-button>
ここで : は'import_employee'
識別子からのものです
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-OHL0NZor-1673401451136)(asset/image-20210428165654008.png)]
権限制御プロセスのポイントのまとめ
ビジネスシーン
社内にはさまざまな機能部門があり、すべてが同じシステムを使用しており、部門ごとにシステム内で必要な操作が異なります。
従業員の役割に応じて異なる権限を設定する必要がある
RBAC 権限の設計アイデア
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-Zjgpc0W3-1673401451136)(asset/image-20210427155035187.png)]
役割ベースの設計アイデア
- 従業員の役割を構成する (従業員は複数の役割を持つことができます)
- ロールのアクセス許可ポイントを構成する (ロールには複数のアクセス許可ポイントを設定できます)
従業員が役割を持っている限り、その役割にバインドされたすべての権限ポイントが自動的に付与されます。
3. 権限設計思想に基づく対応業務モジュール
- 従業員管理
- 役割管理
- 許可ポイントの管理
従業員は許可データを取得します
従業員情報インターフェースには、現在の従業員のすべての権限データが含まれています
userInfo:{
roles: {
menus: [], // 菜单权限数据
points: [] // 按钮权限数据
}
}
権限データを使用して特定の権限処理を実行する
-
メニュー権限制御
ログイン > メニュー権限データ > すべてのローカル動的ルーティング データと照合 > 権限に従ってフィルタリングされた動的ルーティング データを取得
- ルーティング システムに追加します (コンポーネントaddRoutes はパス ID に従ってレンダリングできます)
- 左側のメニューのレンダリングに追加 (vuex 管理 + v-for traversal)
-
ボタン許可制御
ログイン > ボタン権限データ > ボタンの個別の権限 ID を使用して権限データを検索します
カスタムディレクティブ