Idée de conception d'autorisation RBAC
Cible
Comprendre le modèle d'autorisation de RBAC
arrière-plan
Afin d'atteindre 不同的帐号登陆系统后能看到不同的页面,能执行不同的功能
l'objectif, nous avons de nombreuses solutions, le modèle d'autorisation RBAC (Role-Based Access control), qui est une solution d'attribution d'autorisation basée sur les rôles.
Son mode d'autorisation est le suivant :
Trois points clés :
Utilisateur : la personne qui utilise le système
Point d'autorisation : combien de fonctions y a-t-il dans ce système (exemple : il y a 3 pages, chaque page a des opérations différentes)
Rôle : une collection de différents points d'autorisation
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-CxmEMg8V-1673401451130)(asset/image-20210427155035187.png)]
- Attribuer des rôles aux utilisateurs
- Attribuer des autorisations aux rôles
En entreprise réelle :
-
Attribuez d'abord à l'employé un rôle spécifique
-
Attribuez ensuite des points d'autorisation spécifiques au rôle (le bouton d'opération sous la page de salaire de la page de salaire)
Les employés ont des points d'autorité
Rôle d'affectation d'employé - Composant de couche contextuelle
arrière-plan
À l'heure actuelle, il existe déjà certains rôles dans le système. Ensuite, nous attribuerons ces rôles à différents employés afin qu'ils puissent faire différentes choses après être entrés dans le système.
La relation entre les utilisateurs et les rôles est ** 1对多
** : un utilisateur peut avoir plusieurs rôles, il devra donc préciser les permissions de ces multiples rôles. Par exemple, le président de la société peut avoir les rôles de superviseur financier et de capitaine de sécurité : le président peut lire les états financiers, et peut également contrôler le suivi.
Cible
Sur la page de gestion des employés, lorsque vous cliquez sur Attribuer un rôle , le composant s'ouvre/ferme sous la forme d'une couche contextuelle
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-kH47ePb8-1673401451131)(asset/permissionUse/18.png)]
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-aQ1JfSXk-1673401451131)(asset/permissionUse/03.png)]
train de pensée
-
Divisez les fonctions spécifiques (les fonctions du rôle sont plus compliquées et la division des composants réduira la charge de travail)
-
Contrôlez l'affichage via la couche contextuelle
Nouveau composant de gestion des rôles
Créez un fichier ** employees/assignRole.vue
**, le contenu du modèle est le suivant
<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>
Enregistrer et utiliser des composants
Dans la page d'accueil de la gestion des employés employee.vue, introduisez les composants ajoutés ci-dessus
import AssignRole from './assignRole'
components: {
// 省略其他....
AssignRole // 注册组件
},
// 使用
<el-dialog :visiable.sync="showDialogRole">
<assign-role/>
</el-dialog>
Les éléments de données supplémentaires contrôlent l'affichage et le masquage de la couche contextuelle
data () {
return {
// 省略其它
showDialogRole: false
}
}
Rôles d'affectation des employés - Interactions de base
Cible
Fin de l'affichage de l'effet de la fermeture du calque contextuel
Effet d'interaction - afficher la couche contextuelle
Cliquez sur le bouton d'attribution de rôle, enregistrez l'identifiant et affichez la couche de puces.
modèle
<el-button type="text" size="small" @click="hAssignRole(scope.row)">分配角色</el-button>
le code
hEditRole({
id}) {
console.log('当前要分配角色id是', id)
this.showRoleDialog = true
}
Effet d'interaction - fermer le calque contextuel
Les opérations suivantes entraîneront la fermeture de la popup :
- L'utilisateur a cliqué sur le bouton d'annulation
- L'utilisateur a cliqué sur le bouton OK et l'opération a réussi
- L'utilisateur clique sur le X dans le coin supérieur droit de la fenêtre contextuelle
<el-dialog
title="分配角色"
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="showDialogRole"
>
<assign-role @close="showDialogRole=false" />
</el-dialog>
Sous-ensemble
<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>
Rôles d'affectation des employés - Obtenez une liste des rôles et affichez-les avec el-checkbox
Composant : employés/assignRole.vue
Cible
Envoyez une demande pour obtenir une liste de tous les rôles dans ce système et les afficher dans el-checkbox-group
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-SHM57fGx-1673401451132)(asset/07-1619407883065.png)]
train de pensée
- Préparez des modèles statiques et apprenez el-checkbox-group
- Préparer l'interface API
- Envoyez une demande pour obtenir les données du backend, puis affichez
Apprendre la boîte de sélection multiple el-checkbox-group
modèle
<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>
Pour le el-checkbox-group utilisé pour représenter plusieurs choix :
- La valeur de v-model est un tableau (indique une sélection multiple)
- L'attribut label de son élément enfant el-checkbox détermine la valeur après avoir sélectionné cet élément
données
data () {
return {
roleIds: [] // 保存当前选中的权限列表
}
}
Préparez l'API pour obtenir la liste des rôles
Le but est : obtenir tous les personnages. Cependant, le backend ne fournit pas d'interface prête à l'emploi pour obtenir directement tous les rôles.
Remarque : nous n'avons pas de liste de rôles dédiés à la fonction actuelle, nous pouvons utiliser temporairement la pageSize à 100 (équivalent à prendre la première page, 100 éléments par page) pour obtenir des données. dans le dossier src\api\setting.js
,
/**
* 获取所有角色信息
* @param {*} params {page, pagesize}
* @returns
*/
export function getRoles(params) {
return request({
url: '/sys/role',
method: 'GET',
params: params
})
}
Appelé dans le composant métier
en 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>
Afficher les données dans le modèle
<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>
Remarque : l'étiquette détermine la valeur actuellement sélectionnée
résumé
Rôles d'affectation des employés - récupérer les données et remplir
Cible
Si l'utilisateur actuel a déjà configuré certaines données de rôle, les données de rôle configurées doivent être affichées en premier : Certaines cases sont cochées !
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-YLTZ10fZ-1673401451132)(asset/08-1619407893456.png)]
train de pensée
Transmettez l'identifiant de l'utilisateur dans le composant parent
Après avoir ouvert la couche contextuelle, obtenez les informations de rôle actuelles en fonction de l'ID utilisateur, puis faites écho
père en fils - père
définir l'élément de données
data() {
return {
// 省略其他 ...
curEmployeId: '', // 当前的员工编号
}
}
Lorsque vous cliquez sur attribuer un rôle, attribuez-lui une valeur
// 用户点击分配角色
hAssignRole(id) {
this.showDialogRole = true
this.curEmployeId = id
}
modèle
Passer les accessoires aux composants enfants
<el-dialog :visible.sync="showDialogRole" title="权限">
<assign-role
+ :employee-id="curEmployeId"
@close="showDialogRole=false"
/>
</el-dialog>
père en fils - fils à recevoir
<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>
Problème de remplissage de rôle d'affectation d'employé : créé n'est exécuté qu'une seule fois
raison
Étant donné que le sous-composant est imbriqué dans la boîte de dialogue, il ne sera créé qu'une seule fois : créé n'est exécuté qu'une seule fois, et les opérations d'affichage et de masquage suivantes n'entraînent pas la reconstruction du composant, donc : le contenu ouvert ultérieurement est le même que la première fois .
résoudre
Solution 1 : Lorsque la couche contextuelle est masquée, détruisez le sous-composant.
<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>
Avantages : Simple ; Inconvénients : La destruction de composants pose certains problèmes de performances
Option II :
Idée : Lorsque vous cliquez pour attribuer un rôle dans le composant parent, appelez directement la méthode dans le composant enfant pour obtenir les données.
Ajouter une référence au composant enfant
<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)
})
}
Supprimer créé dans le prix du sous-groupe
// created() {
// // 组件创建时执行一次
// this.loadRoles()
// },
Rôle d'affectation d'employé - Enregistrer
Cible
Enregistrer les fonctions spécifiques du rôle attribué modifié à l'utilisateur
train de pensée
Interface d'encapsulation -> interface d'appel
Attribuer une interface de rôle
Dans le api/employees.js
fichier ** **, ajoutez une méthode appelée assignRoles
/**
* @description: 为用户分配角色
* @param {*} data { id:当前用户id, roleIds:选中的角色id组成的数组 }
* @return {*}
*/
export function assignRoles(data) {
return request({
url: '/sys/user/assignRoles',
data,
method: 'put'
})
}
Confirmer la sauvegarde dans le code entreprise
Importer l'API définie ci-dessus
import {
assignRoles } from '@/api/employees'
Ajouter un événement de clic au bouton
<template>
<el-button type="primary" size="small" @click="hSave">确定</el-button>
</template>
Rappels supplémentaires enregistrés
// 保存当前角色信息
async hSubmit() {
const res = await assignRoles({
id: this.employeeId, roleIds: this.roleIds })
console.log('保存角色', res)
this.$emit('update-close')
}
Dans le composant parent, écoutez l'événement
<assign-role
ref="assignRole"
:employee-id="curEmployeId"
@update-close="hUpdateClose"
@close="showDialogRole=false"
/>
hMise à jourFermer :
// 用户分配角色成功
hUpdateClose() {
this.showDialogRole = false
this.loadEmployeeList()
}
Autorisations d'attribution de rôle - Description générale
Pourquoi attribuer des autorisations aux rôles
Quel est le rôle de l'utilisateur, il a certaines fonctions
Dans le code précédent, un rôle a été ajouté à l'utilisateur, donc ce que l'employé peut faire dépend des fonctions spécifiques exercées dans le rôle.
Il existe de nombreuses fonctions de gestion des droits, qui nécessitent des composants de conditionnement.
Autorisations d'attribution de rôle - Composants de l'espace contextuel et interactions de base
Cible
Dans le module de gestion des rôles (views/settings/settings.vue), implémentez les sous-composants
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-0zTwqJw2-1673401451132)(asset/assign permissions.gif)]
train de pensée
Préparer le popup -> enregistrer l'événement -> fournir la méthode de données
Terminer l'attribution des points d'autorité aux rôles
Encapsuler les sous-composants
Encapsulez d'abord un composant assignPermission.vue sous les paramètres de sauvegarde.
|--settings
|---------settings.vue # 角色管理主页
|---------assignPermission.vue #给角色分配权限
Il sera référencé et utilisé dans settings.vue.
Ajouter une couche de bombe au composant parent et introduire le composant enfant
Introduire des sous-composants dans settings.vue
import assignPermission from './assignPermission'
enregistrer
components: {
assignPermission
},
Ajoutez le composant el-dialog au modèle et importez-le
<!-- 分配权限的弹层 -->
<el-dialog
title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)" :visible.sync="showDialogAssign">
<assign-permission />
</el-dialog>
Données supplémentaires
return {
//... 省略其它
showDialogAssign: false, // 分配权限对话框
}
Interaction - afficher la couche contextuelle
Afficher la fenêtre contextuelle. Ajouter un événement de clic au bouton
<el-button size="small" type="success" @click="hAssign">
分配权限
</el-button>
Définissez showDialogAssign sur true dans le rappel
methods:{
hAssign() {
this.showDialogAssign = true
}
}
Couche contextuelle de masquage d'interaction
Événement personnalisé : enfant à parent
<el-dialog
title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)"
:visible.sync="showDialogAssign"
>
<assign-permission
+ @close="showDialogAssign=false"
/>
</el-dialog>
dans le composant enfant
methods: {
hCancel() {
// 通过父组件去关闭弹层
this.$emit('close')
}
}
Autorisations d'attribution de rôle - Obtenir des données de point d'autorisation et les afficher
Cible
Dans le composant assignPermission.vue, toutes les données des points d'autorisation du système actuel sont obtenues et affichées dans une arborescence. L'effet cible est le suivant :
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme de lien antivol, il est recommandé d'enregistrer l'image et de la télécharger directement (img-oYbV8FQO-1673401451133)(asset/image-20210428100556359.png)]
train de pensée
-
Préparer l'interface du point d'autorisation
-
Après l'affichage de la popup :
- Appelez l'API pour envoyer une requête afin d'obtenir des données ;
- Conversion de format de données (tableau en arbre)
- Liaison de modèle (afficher les données sur el-tree)
préparer l'API
Préparez l'API dans src\api\permission.js (cette API a été utilisée sur la page des points d'autorisation)
import request from '@/utils/request'
// 获取权限点列表
export function getPermissionList(params) {
return request({
url: '/sys/permission',
params
})
}
Préparer les éléments de données
permissionData: [] // 存储权限数据
Envoyer une demande pour obtenir des données
méthode d'importation
import {
getPermissionList } from '@/api/permission'
import {
tranListToTreeData } from '@/utils/index'
Appelé créé
created() {
this.loadPermissionList()
},
async loadPermissionList() {
// 发送请求, 获取权限列表
const { data } = await getPermissionList()
console.log('权限列表的数据是', data)
this.permissionData = tranListToTreeData(data)
}
Afficher les données dans el-tree
<!-- 权限点数据展示 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
/>
Remarque : accessoires
Autorisations d'attribution de rôle - Définition des propriétés d'el-tree
Cible
Autres paramètres pour el-tree :
- Afficher la boîte de sélection
- Tout développer par défaut
- Fermer l'association parent-enfant
configuration d'attribut
https://element.eleme.io/#/zh-CN/component/tree
- Afficher la case à cocher Afficher la case à cocher
- default-expand-all Développer par défaut
- check-strictly set true, vous pouvez fermer l'association parent-enfant
<!-- 权限点数据展示 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
default-expand-all
:show-checkbox="true"
:check-strictly="true"
/>
La méthode d'écriture par défaut-expand-all est équivalente à:default-expand-all="true"
Effet
Autorisations d'attribution de rôle - Remplissage de données
Cible
L'utilisateur actuel peut avoir certaines autorisations existantes, qui doivent être renvoyées en écho
train de pensée
- préparer l'API
- Assemblez les paramètres actuels et appelez l'api pour obtenir les données ;
- Afficher les données de remplissage dans l'arborescence
préparer l'API
Fichier : dans src\api\settings.js, ajoutez une méthode getRoleDetail
/**
* @description: 获取角色详情
* @param {*} id 角色id
* @return {*}
*/
export function getRoleDetail(id) {
return request({
url: `/sys/role/${
id}`
})
}
Passer l'identifiant du parent à l'enfant
Dans le composant parent setting.vue, définissez l'élément de données :
data () {
return {
// 省略其他...
roleId: ''
}
}
Enregistrez le roleId lorsque vous cliquez pour attribuer des autorisations
<el-button size="mini" type="success" @click="hAssign(scope.row.id)">分配权限</el-button>
Le rappel correspondant est :
hAssign(id) {
// 记下来id
this.roleId = id
this.showDialogAssign = true
},
Recevoir roleId dans la sous-classe
Dans assignPerimission.vue, les accessoires de définition supplémentaires reçoivent la valeur roleId
props: {
roleId: {
type: String,
required: true
}
}
Appelez api pour obtenir des données
Introduire l'API précédemment encapsulée
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)
},
Remplir les données dans el-tree
Les données ont été obtenues, comment les remplir dans l'arbre el, pour que certaines cases à cocher soient cochées ?
答: setCheckedKeys + clé de nœud
Site officiel : https://element.eleme.io/#/zh-CN/component/tree#fang-fa
- Ajouter la clé de nœud d'attribut à l'arborescence
<!-- 权限点数据展示 -->
<el-tree
ref="refTree"
:data="permissionData"
:props="{ label: 'name' }"
:default-expand-all="true"
:show-checkbox="true"
:check-strictly="true"
node-key="id"
/>
- appeler setCheckedKeys
// 获取角色现有的权限
async loadRoleDetail() {
const res = await getRoleDetail(this.roleId)
console.log('获取角色现有的权限', res.data.permIds)
// 回填
this.$refs.refTree.setCheckedKeys(res.data.permIds)
},
résumé
- Dans le composant el-tree, renvoyez les données au composant el-tree via la méthode setCheckedKeys
Autorisations d'attribution de rôle - problème de remplissage de données : la création n'est exécutée qu'une seule fois
raison
Étant donné que le sous-composant est imbriqué dans la boîte de dialogue, il ne sera créé qu'une seule fois : créé n'est exécuté qu'une seule fois, et les opérations d'affichage et de masquage suivantes n'entraînent pas la reconstruction du composant, donc : le contenu ouvert ultérieurement est le même que la première fois .
résoudre
Solution 1 : Lorsque la couche contextuelle est masquée, détruisez le sous-composant.
<el-dialog
title="分配角色"
:visible.sync="showDialogRole"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<子组件
+ v-if="showDialogAssign"
/>
</el-dialog>
Avantages : simple, les dernières données sont obtenues ;
Inconvénients : La destruction des composants pose certains problèmes de performances,
Solution 2 : faites référence aux sous-composants via des références et appelez directement ses méthodes pour envoyer des requêtes
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (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()
})
}
}
Autorisations d'attribution de rôle - Enregistrer les paramètres
Cible
Compléter la fonction d'attribution d'autorisation
train de pensée
Préparez l'api, appelez quand cliquez sur enregistrer
préparer l'API
Dans le fichier src\api\settings.js, ajoutez une API pour attribuer des autorisations
/**
* 给角色分配权限
* @param {*} data {id:角色id, permIds:[] 所有选中的节点的id组成的数组}
* @returns
*/
export function assignPerm(data) {
return request({
url: '/sys/role/assignPrem',
method: 'put',
data
})
}
Appelez l'api pour attribuer des autorisations-analyse
Appelez simplement l'API définie ci-dessus et transmettez les paramètres pertinents.
Il y a deux paramètres ici :
- Quel est l'ID de rôle actuel ?
在点击分配权限时,可以从表格中获取, 父传子
- Quel est l'ID de liste d'autorisations correspondant ?
通过el-tree组件的getCheckedKeys来获取用户选中的id列表
Appel api pour attribuer des autorisations - réalisation de la fonction
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([])
}
Enfin, lorsque la couche pop-up est fermée, effacez les données sélectionnées par l'utilisateur dans l'arborescence
résumé
- el-tree récupère les clés du nœud actuellement sélectionné : getCheckedKeys
- Pour les composants el-tree, effacez la sélection actuelle : this.$refs.tree.setCheckedKeys([])
Connaître les données d'autorisation de l'utilisateur
Jusqu'à présent, nous avons implémenté tous les aspects de l'idée de conception d'autorisation RBAC. Nous avons attribué des rôles aux employés et attribué des points d'autorisation aux rôles. Les employés ont maintenant des points d'autorisation correspondants. Ensuite, nous pouvons utiliser ces points d'autorisation pour le contrôle d'autorité réel, dans le projet RH, il existe deux lieux de contrôle d'autorité :
- Contrôle d'autorisation du menu gauche (après que différents utilisateurs entrent dans le système, les menus qu'ils voient sont différents)
- Contrôle des autorisations des boutons d'opération (boutons sur la page, différentes personnes ont des autorisations différentes)
Où sont les données d'autorisation
Créez de nouvelles données d'employé dans la gestion des employés, puis utilisez le nouveau compte d'employé pour vous connecter (le mot de passe est 123456) et affichez les données renvoyées de l'interface d'informations personnelles (/api/sys/profile), comme indiqué dans la figure ci-dessous, aucune autorisation n'est configurée Le statut de retour du , vous pouvez voir que les menus et les points sous les rôles sont vides et que les employés n'ont aucune autorisation pour le moment
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-WN9TIr43-1673401451134)(asset/permissionUse/13.png)]
Comment modifier les données d'autorisation
Connectez-vous avec un compte administrateur, puis attribuez deux autorisations de menu et une autorisation de bouton d'opération au nouvel employé nouvellement créé, puis connectez-vous à nouveau au compte de l'employé pour afficher les informations personnelles et renvoyer les données.
Pas:
-
Gestion des points d'autorisation > Ajouter des points d'autorisation d'opération de bouton sous la gestion des employés
导入,导出
-
Gestion des rôles > Nouveau rôle Directeur du personnel > Attribuer des autorisations aux rôles (gestion des employés, importation, exportation)
-
Gestion des employés > Attribuer le rôle de DRH aux employés
-
Connectez-vous à nouveau au nouveau compte d'employé, vérifiez les données d'autorisation et observez les données.rôles.menus, éléments de points
Analyse globale du menu de gauche généré dynamiquement par l'application d'autorisation
analyser
La connexion est réussie, entrez dans la garde de navigation :
- Obtenir des informations d'autorisation personnelles
- Générer des routes dynamiques accessibles
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-wMfe8si3-1673401451134)(asset/image-20210428122059657.png)]
exemple
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-L1oHNZ59-1673401451134)(asset/permissionUse/17.png)]
Demande d'autorisation - générer dynamiquement le menu de gauche - méthode addRoutes
Cible
Apprenez addRoutes dans l'objet vue-router et utilisez-le pour ajouter dynamiquement des configurations de routage
train de pensée
Les pages (configuration de routage) auxquelles les utilisateurs peuvent accéder doivent être dynamiques , vous devez donc au préalable maîtriser une API capable d'ajouter dynamiquement des adresses de routage
utilisation de base d'addRoutes
Format
router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])
Rôle : ajouter dynamiquement une configuration de routage
exemple
// 按钮
<button @click="hAddRoute">addRoute</button>
// 回调
hAddRoute() {
this.$router.addRoutes([{
path: '/abc',
component: () => import('@/views/abc'),
}])
},
Effet
Après avoir cliqué sur le bouton, vous pouvez accéder à /abc dans l'adresse.
code de rénovation
-
Supprimer la partie du routage dynamique dans la configuration du routage dans router/index.js
const createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), // routes: constantRoutes // 合并动态和静态的路由 , ...asyncRoutes - routes: [...constantRoutes, ...asyncRoutes] + routes: [...constantRoutes] })
-
Introduit dans permission.js et ajouté dynamiquement à l'aide de addRoutes
Transformer la table de routage dynamique qui a été directement écrite statiquement dans le routeur sous la
addRoutes
forme ajoutée par appel de méthode
// 引入所有的动态路由表(未经过筛选)
+ 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()
})
Effet d'acceptation
-
Seule la page d'accueil statique est laissée dans le menu de gauche (à résoudre plus tard)
-
La saisie manuelle d'une certaine adresse de routage dynamique dans le navigateur est toujours disponible, ce qui prouve que nous avons effectivement ajouté le routage dynamique à notre système de routage.
Demande d'autorisation - générer dynamiquement le menu de gauche - réécrire l'emplacement de sauvegarde du menu
analyse du problème
Les données utilisées par le rendu de menu actuel (src\layout\components\Sidebar\index.vue) : this.$router.options.routes
ces données sont fixes et la table de routage que nous avons ajoutée via addRoutes n'existe qu'en mémoire et ne changera pasthis.$router.options.routes
Si nous voulons que les données de routage soient reflétées dans le menu immédiatement après l'appel de la méthode addRoutes, nous devons penser à une méthode supplémentaire. Pensez-y. Dans le développement de Vue, quelle technologie peut garantir que les fonctionnalités réactives peuvent également être modifiées dynamiquement ? vuex !
Cible
enregistrer les données du menu dans vuex
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme de lien antivol, il est recommandé d'enregistrer l'image et de la télécharger directement (img-4Q4i5Ywp-1673401451134)(asset/image-20210428122540218.png)]
Définir les données du menu de gestion vuex
- Modules supplémentaires.
src/store/modules
Ajoutez le module menu.js ci-dessous- Définir la liste des menus de données
- Méthode pour modifier les données setMenuList
// 导入静态路由
import {
constantRoutes } from '@/router'
export default {
namespaced: true,
state: {
// 先以静态路由作为菜单数据的初始值
menuList: [...constantRoutes]
},
mutations: {
setMenuList(state, asyncRoutes) {
// 将动态路由和静态路由组合起来
state.menuList = [...constantRoutes, ...asyncRoutes]
}
}
}
Bien sûr, enregistrez ce module dans src/store/index.js
+ import menu from './modules/menu'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
settings,
user,
+ menu
},
getters
})
2. Soumettez setMenuList pour générer des données de menu complètes
Modifier le code dans src/permission.js
if (!store.getters.userId) {
await store.dispatch('user/getUserInfo')
// 动态添加可以访问的路由设置
router.addRoutes(asyncRoutes)
// 根据用户实际能访问几个页面来决定从整体8个路由设置
// 中,过滤中出来几个,然后保存到vuex中
store.commit('menu/setMenuList', asyncRoutes)
}
3. La partie génération de menu est réécrite pour utiliser les données dans vuex
Dans le fichier src\layout\components\Sidebar\index.vue, modifiez
routes() {
// 拿到的是一个完整的包含了静态路由和动态路由的数据结构
// return this.$router.options.routes
return this.$store.state.menu.menuList
}
résumé
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme de lien antivol, il est recommandé d'enregistrer l'image et de la télécharger directement (img-FQFaCeh7-1673401451135)(asset/image-20210525124226388.png)]
Demande d'autorisation - utiliser les données d'autorisation pour le filtrage
Cible
Dans l'étape précédente, nous avons réalisé:
- Le routage dynamique est ajouté dynamiquement au système de routage via addRoutes
- Enregistrer la route dynamique dans le menu de vuex
Cependant, nous n'avons pas fait correspondre les données d'autorisation. Ensuite, nous filtrons le menu dynamique à travers les données d'autorisation renvoyées par l'interface pour nous assurer que le menu complété est lié à l'autorisation de l'utilisateur.
filtrer les idées
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme de lien antivol, il est recommandé d'enregistrer l'image et de la télécharger directement (img-JQ1M2Yrk-1673401451135)(asset/image-20210525000107003.png)]
Le filtrage utilise le nom comme identifiant et vérifie si le nom de la route est cohérent avec l'indice
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-c8GBacDW-1673401451135)(asset/image-20210428153438963.png)]
La convention d'interface du backend est la suivante :
- Nom de la page : ID de l'employé : employés
- Nom de la page : ID d'autorisation : autorisations
- Nom de la page : Identifiant de la structure organisationnelle : départements
- Nom de la page : ID des paramètres : paramètres
- Nom de la page : ID de salaire : salaires
- Nom de la page : ID d'approbation : approbations
- Nom de la page : Présences Logo : présences
- Nom de la page : Logo de la sécurité sociale : sécurités_sociales
Renvoyer les éléments de menu des actions
Les pages auxquelles l'utilisateur peut accéder sont obtenues via des actions et n'ont qu'à revenir de l'action.
Modifier store/modules/user.js
et compléter la déclaration de retour.
// 用来获取用户信息的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
},
Obtenir la valeur de retour de l'action dans permission.js et filtrer
en 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)
}
Effet
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-EgzHy9V4-1673401451135)(asset/permissionUse/15.png)]
résumé
- Obtenir la valeur de retour des actions
asyncRoutes.filter
Corrections de bugs lors du rafraichissement de la page
question
Si nous actualisons le navigateur, nous constaterons que nous sommes passés à la page 404
Pour l'itinéraire ajouté par addRoute, l'écran sera blanc lors du rafraîchissement
raison
Désormais, la page 404 de notre configuration de routage se trouve au milieu et non à la fin de toutes les routes.
résoudre
Changez simplement la page 404 à la fin de la configuration de routage
le code
-
path:'*'
Supprimer cet élément des routes statiques dans routes/index.js -
Ajouté à la fin dans 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()
}
Réinitialiser les itinéraires à la déconnexion
question
Après avoir quitté, reconnectez-vous et constatez que le menu est anormal (la console a une sortie indiquant que l'itinéraire est répété);
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-xfeMibRM-1673401451136)(asset/image-20210525161618731.png)]
raison
Les paramètres de routage sont router.addRoutes(filterRoutes)
ajoutés via , et lorsque vous vous déconnectez, ils ne sont pas effacés, et vous vous reconnectez et les ajoutez à nouveau, il y a donc une duplication.
Il est nécessaire de réinitialiser l'autorité de routage (restaurer la valeur par défaut) et de l'ajouter à nouveau après la connexion à l'avenir, sinon, elle sera ajoutée à plusieurs reprises
résoudre
Nos router/index.js
fichiers **, ont trouvé une méthode de réinitialisation de l'itinéraire
// 重置路由
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
Cette méthode consiste à ré-instancier la route, ce qui équivaut à changer une nouvelle route. La précédente ** 加的路由
n'existe plus. Vous devez l'appeler lorsque vous vous déconnectez **
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 })
}
Demande d'autorisation - Contrôle au niveau du bouton - Analyse
Cible
L'employé A et l'employé B peuvent accéder à la même page (prenons la gestion des employés comme exemple), mais l'employé A peut exporter Excel, mais l'employé B ne peut pas exporter Excel
train de pensée
Une fois que l'utilisateur s'est connecté avec succès, les autorisations au niveau des boutons auxquelles l'utilisateur peut accéder sont stockées dans le tableau de points. Et ces données sont stockées dans vuex, elles sont donc accessibles n'importe où dans le projet.
- Si le logo sur un bouton apparaît à des points, il peut être affiché
Demande d'autorisation - Contrôle du niveau des boutons - Instructions personnalisées
Directives : v-pour, v-si…
Instructions définies par l'utilisateur : instructions auto-définies, car les instructions elles-mêmes ne suffisent pas, nous devons donc les définir nous-mêmes.
Utilisez-le pour le contrôle des autorisations au niveau des boutons
Examiner les directives personnalisées
formulaire d'inscription
// 注册一个全局自定义指令 `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()
}
})
utiliser le format
<input v-foucs="'xxxx'" />
Résoudre la vérification des autorisations au niveau du bouton
Dans main.js, définissez la directive globale
// 注册一个全局自定义指令 `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'
}
}
})
utiliser
<el-button
+ v-allow="'import_employee'"
type="warning"
size="small"
@click="$router.push('/import')"
>导入excel</el-button>
Ici le : 'import_employee'
provient de l'identifiant
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé de sauvegarder l'image et de la télécharger directement (img-OHL0NZor-1673401451136)(asset/image-20210428165654008.png)]
Synthèse des points clés du processus de contrôle d'autorité
Scène d'affaires
Il existe différents départements fonctionnels dans l'entreprise, qui utilisent tous le même système, et les choses dont les personnes de différents départements ont besoin pour fonctionner dans le système sont différentes
Il est nécessaire de configurer différentes autorisations en fonction des différents rôles des employés
Idée de conception d'autorisation RBAC
[Le transfert d'image du lien externe a échoué, le site source peut avoir un mécanisme anti-leeching, il est recommandé d'enregistrer l'image et de la télécharger directement (img-Zjgpc0W3-1673401451136)(asset/image-20210427155035187.png)]
Une idée de conception basée sur les rôles
- Configurer les rôles des employés (un employé peut avoir plusieurs rôles)
- Configurer les points d'autorisation pour les rôles (un rôle peut avoir plusieurs points d'autorisation)
Tant que les employés ont un rôle, ils ont automatiquement tous les points d'autorité liés au rôle
3. Modules métier correspondants basés sur des idées de conception d'autorité
- gestion des employés
- gestion des rôles
- Gestion des points d'autorisation
Les employés obtiennent des données d'autorisation
L'interface d'informations sur les employés contient toutes les données d'autorisation de l'employé actuel
userInfo:{
roles: {
menus: [], // 菜单权限数据
points: [] // 按钮权限数据
}
}
Utiliser les données d'autorisation pour effectuer un traitement d'autorisation spécifique
-
Contrôle des autorisations de menu
Connexion> données d'autorisation de menu> correspondance avec toutes les données de routage dynamique locales> obtenir des données de routage dynamique filtrées en fonction des autorisations
- Ajouter au système de routage (le composant addRoutes peut être rendu en fonction de l'ID de chemin )
- Ajouté au rendu du menu de gauche (gestion vuex + v-for traversal)
-
Contrôle des autorisations des boutons
Connexion > Données d'autorisation du bouton > Utiliser l'ID d'autorisation séparé du bouton pour effectuer une recherche dans les données d'autorisation
directive personnalisée