Idea de diseño de permiso RBAC
Objetivo
Comprender el modelo de permisos de RBAC
fondo
Para lograr 不同的帐号登陆系统后能看到不同的页面,能执行不同的功能
el objetivo, tenemos muchas soluciones, el modelo de permisos RBAC (control de acceso basado en roles), que es una solución de asignación de permisos basada en roles.
Su modo de permiso es el siguiente:
Tres puntos clave:
Usuario: la persona que utiliza el sistema
Punto de permiso: cuántas funciones hay en este sistema (ejemplo: hay 3 páginas, cada página tiene diferentes operaciones)
Rol: una colección de diferentes puntos de permiso
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-CxmEMg8V-1673401451130)(asset/image-20210427155035187.png)]
- Asignar roles a los usuarios
- Asignar permisos a roles
En el negocio real:
-
Primero asigne al empleado un rol específico
-
Luego asigne puntos de permiso específicos al rol (el botón de operación debajo de la página de salario de la página de salario)
Los empleados tienen puntos de autoridad.
Función de asignación de empleados: componente de capa emergente
fondo
En la actualidad, ya existen algunos roles en el sistema, a continuación, vamos a asignar estos roles a diferentes empleados para que puedan hacer diferentes cosas después de ingresar al sistema.
La relación entre usuarios y roles es ** 1对多
**: un usuario puede tener múltiples roles, por lo que especificará los permisos de estos múltiples roles. Por ejemplo, el presidente de la empresa puede tener las funciones de supervisor financiero y capitán de seguridad: el presidente puede leer los estados financieros y también puede verificar el seguimiento.
Objetivo
En la página de administración de empleados, cuando haga clic en Asignar rol , el componente se abrirá/cerrará en forma de una capa emergente
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-kH47ePb8-1673401451131)(asset/permissionUse/18.png)]
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-aQ1JfSXk-1673401451131)(asset/permissionUse/03.png)]
tren de pensamiento
-
Divida las funciones específicas (las funciones del rol son más complicadas y dividir los componentes reducirá la carga de trabajo)
-
Controla la visualización a través de la capa emergente
Nuevo componente de gestión de roles
Crea un archivo ** employees/assignRole.vue
**, el contenido de la plantilla es el siguiente
<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>
Registrar y usar componentes
En la página de inicio de administración de empleados employee.vue, introduzca los componentes agregados anteriormente
import AssignRole from './assignRole'
components: {
// 省略其他....
AssignRole // 注册组件
},
// 使用
<el-dialog :visiable.sync="showDialogRole">
<assign-role/>
</el-dialog>
Los elementos de datos complementarios controlan la visualización y ocultación de la capa emergente
data () {
return {
// 省略其它
showDialogRole: false
}
}
Roles de asignación de empleados: interacciones básicas
Objetivo
Terminado de mostrar el efecto de cerrar la capa emergente
Efecto de interacción: capa emergente de visualización
Haga clic en el botón Asignar función , registre la identificación y muestre la capa de viñetas.
plantilla
<el-button type="text" size="small" @click="hAssignRole(scope.row)">分配角色</el-button>
el código
hEditRole({
id}) {
console.log('当前要分配角色id是', id)
this.showRoleDialog = true
}
Efecto de interacción: cerrar capa emergente
Las siguientes operaciones harán que la ventana emergente se cierre:
- El usuario hizo clic en el botón cancelar.
- El usuario hizo clic en el botón Aceptar y la operación fue exitosa.
- El usuario hace clic en la X en la esquina superior derecha de la ventana emergente
<el-dialog
title="分配角色"
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="showDialogRole"
>
<assign-role @close="showDialogRole=false" />
</el-dialog>
Subensamblaje
<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>
Roles de asignación de empleados: obtenga una lista de roles y muéstrelos con el-checkbox
Componente: empleados/assignRole.vue
Objetivo
Envíe una solicitud para obtener una lista de todos los roles en este sistema y mostrarlos en el-checkbox-group
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-SHM57fGx-1673401451132)(asset/07-1619407883065.png)]
tren de pensamiento
- Prepara plantillas estáticas y aprende el-checkbox-group
- Preparar la interfaz api
- Envíe una solicitud para obtener los datos de back-end y luego renderice
Aprenda el cuadro de selección múltiple el-checkbox-group
plantilla
<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>
Para el grupo de casillas de verificación el utilizado para representar opciones múltiples:
- El valor de v-model es una matriz (indica selección múltiple)
- El atributo de etiqueta de su elemento secundario el-checkbox determina el valor después de seleccionar este elemento
datos
data () {
return {
roleIds: [] // 保存当前选中的权限列表
}
}
Prepare la API para obtener la lista de roles.
El objetivo es: conseguir todos los personajes. Sin embargo, el backend no proporciona una interfaz preparada para obtener directamente todos los roles.
Nota: No tenemos una lista de roles dedicados a la función actual, podemos usar temporalmente pageSize como 100 (equivalente a tomar la primera página, 100 elementos por página) para obtener datos. en el archivo src\api\setting.js
,
/**
* 获取所有角色信息
* @param {*} params {page, pagesize}
* @returns
*/
export function getRoles(params) {
return request({
url: '/sys/role',
method: 'GET',
params: params
})
}
Llamado en el componente empresarial
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>
Representar los datos en la plantilla
<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>
Nota: la etiqueta determina el valor seleccionado actualmente
resumen
Roles de asignación de empleados: obtener datos y reponer
Objetivo
Si el usuario actual ya ha configurado algunos datos de roles, los datos de roles configurados deben mostrarse primero: ¡algunas casillas de verificación están seleccionadas!
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-YLTZ10fZ-1673401451132)(asset/08-1619407893456.png)]
tren de pensamiento
Pase la identificación de usuario en el componente principal
Después de abrir la capa emergente, obtenga la información del rol actual de acuerdo con la identificación del usuario y luego haga eco
padre a hijo - padre
definir elementos de datos
data() {
return {
// 省略其他 ...
curEmployeId: '', // 当前的员工编号
}
}
Al hacer clic en asignar un rol, asígnele un valor
// 用户点击分配角色
hAssignRole(id) {
this.showDialogRole = true
this.curEmployeId = id
}
plantilla
Pasar accesorios a componentes secundarios
<el-dialog :visible.sync="showDialogRole" title="权限">
<assign-role
+ :employee-id="curEmployeId"
@close="showDialogRole=false"
/>
</el-dialog>
padre a hijo - hijo a recibir
<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>
Problema de reposición de roles de asignación de empleados: creado solo se ejecuta una vez
razón
Dado que el subcomponente está anidado en el cuadro de diálogo, solo se creará una vez: la creación se ejecuta solo una vez, y las operaciones posteriores de visualización y ocultación no harán que se reconstruya el componente, por lo que: el contenido abierto más tarde es el mismo que la primera vez .
resolver
Solución 1: cuando la capa emergente esté oculta, destruya el subcomponente.
<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>
Ventajas: Simple; Desventajas: La destrucción de componentes tiene ciertos problemas de rendimiento
Opción II:
Idea: cuando haga clic para asignar un rol en el componente principal, llame directamente al método en el componente secundario para obtener los datos
Agregar una referencia al componente secundario
<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)
})
}
Eliminar creado en el precio del subgrupo
// created() {
// // 组件创建时执行一次
// this.loadRoles()
// },
Función de asignación de empleados - Guardar
Objetivo
Guardar las funciones específicas del rol asignado modificado del usuario
tren de pensamiento
Interfaz de encapsulación -> interfaz de llamada
Asignar interfaz de rol
En el api/employees.js
archivo ** **, agregue un método llamado AssignRoles
/**
* @description: 为用户分配角色
* @param {*} data { id:当前用户id, roleIds:选中的角色id组成的数组 }
* @return {*}
*/
export function assignRoles(data) {
return request({
url: '/sys/user/assignRoles',
data,
method: 'put'
})
}
Confirmar el guardado en el código de negocio
Importar la API definida anteriormente
import {
assignRoles } from '@/api/employees'
Agregar un evento de clic al botón
<template>
<el-button type="primary" size="small" @click="hSave">确定</el-button>
</template>
Devoluciones de llamada adicionales guardadas
// 保存当前角色信息
async hSubmit() {
const res = await assignRoles({
id: this.employeeId, roleIds: this.roleIds })
console.log('保存角色', res)
this.$emit('update-close')
}
En el componente principal, escucha el evento.
<assign-role
ref="assignRole"
:employee-id="curEmployeId"
@update-close="hUpdateClose"
@close="showDialogRole=false"
/>
hActualizarCerrar:
// 用户分配角色成功
hUpdateClose() {
this.showDialogRole = false
this.loadEmployeeList()
}
Permisos de asignación de funciones: descripción general
Por qué asignar permisos a roles
Cuál es el rol del usuario, tiene ciertas funciones
En el código anterior, se ha agregado un rol al usuario, por lo que lo que el empleado puede hacer depende de las funciones específicas que se llevan a cabo en el rol.
Hay muchas funciones de gestión de derechos que requieren componentes de empaquetado.
Permisos de asignación de roles: componentes Bombshell e interacciones básicas
Objetivo
En el módulo de gestión de roles (views/settings/settings.vue), implemente subcomponentes
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-0zTwqJw2-1673401451132)(activo/asignar permisos.gif)]
tren de pensamiento
Preparar ventana emergente -> registrar evento -> proporcionar método de datos
Complete el negocio de asignar puntos de autoridad a roles
Encapsular subcomponentes
Primero encapsule un componente de AssignPermission.vue en la configuración para la copia de seguridad.
|--settings
|---------settings.vue # 角色管理主页
|---------assignPermission.vue #给角色分配权限
Será referenciado y utilizado en settings.vue.
Agregue una capa emergente en el componente principal e introduzca el componente secundario
Introducir subcomponentes en settings.vue
import assignPermission from './assignPermission'
registro
components: {
assignPermission
},
Agregue el componente el-dialog a la plantilla e impórtelo
<!-- 分配权限的弹层 -->
<el-dialog
title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)" :visible.sync="showDialogAssign">
<assign-permission />
</el-dialog>
Dato suplementario
return {
//... 省略其它
showDialogAssign: false, // 分配权限对话框
}
Interacción: muestra la capa emergente
Mostrar ventana emergente. Agregar un evento de clic al botón
<el-button size="small" type="success" @click="hAssign">
分配权限
</el-button>
Establezca showDialogAssign en verdadero en la devolución de llamada
methods:{
hAssign() {
this.showDialogAssign = true
}
}
Capa emergente de ocultación de interacción
Evento personalizado: niño a padre
<el-dialog
title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)"
:visible.sync="showDialogAssign"
>
<assign-permission
+ @close="showDialogAssign=false"
/>
</el-dialog>
en el componente hijo
methods: {
hCancel() {
// 通过父组件去关闭弹层
this.$emit('close')
}
}
Permisos de asignación de roles: obtenga datos y visualización de puntos de permiso
Objetivo
En el componente AssignPermission.vue, todos los datos de puntos de permiso en el sistema actual se obtienen y se muestran en una estructura de árbol. El efecto objetivo es el siguiente:
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-oYbV8FQO-1673401451133)(activo/imagen-20210428100556359.png)]
tren de pensamiento
-
Preparar la interfaz del punto de permiso
-
Después de que se muestre la ventana emergente:
- Llame a la API para enviar una solicitud para obtener datos;
- Conversión de formato de datos (matriz a árbol)
- Enlace de plantilla (mostrar datos en el-tree)
preparar API
Prepare la API en src\api\permission.js (esta API se ha utilizado en la página del punto de permiso)
import request from '@/utils/request'
// 获取权限点列表
export function getPermissionList(params) {
return request({
url: '/sys/permission',
params
})
}
Preparar elementos de datos
permissionData: [] // 存储权限数据
Enviar una solicitud para obtener datos
método de importación
import {
getPermissionList } from '@/api/permission'
import {
tranListToTreeData } from '@/utils/index'
Llamado en creado
created() {
this.loadPermissionList()
},
async loadPermissionList() {
// 发送请求, 获取权限列表
const { data } = await getPermissionList()
console.log('权限列表的数据是', data)
this.permissionData = tranListToTreeData(data)
}
Mostrar datos en el-tree
<!-- 权限点数据展示 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
/>
Nota: accesorios
Permisos de asignación de funciones: configuración de las propiedades de el-tree
Objetivo
Otras configuraciones para el-tree:
- Mostrar cuadro de selección
- Expandir todo por defecto
- Asociación estrecha de padres e hijos
configuración de atributos
https://element.eleme.io/#/zh-CN/component/tree
- mostrar casilla de verificación mostrar casilla de verificación
- default-expand-all Expandir por defecto
- check-strictly set verdadero, puede cerrar la asociación padre-hijo
<!-- 权限点数据展示 -->
<el-tree
:data="permissionData"
:props="{ label: 'name' }"
default-expand-all
:show-checkbox="true"
:check-strictly="true"
/>
El método de escritura por defecto-expandir-todo es equivalente a:default-expand-all="true"
Efecto
Permisos de asignación de funciones - Reposición de datos
Objetivo
El usuario actual puede tener algunos permisos existentes, que deben repetirse
tren de pensamiento
- preparar API
- Reúna los parámetros actuales y llame a la API para obtener los datos;
- Mostrar el relleno de datos en el árbol
preparar API
Archivo: en src\api\settings.js, agregue un método getRoleDetail
/**
* @description: 获取角色详情
* @param {*} id 角色id
* @return {*}
*/
export function getRoleDetail(id) {
return request({
url: `/sys/role/${
id}`
})
}
Pasar id de padre a hijo
En el componente principal setting.vue, defina el elemento de datos:
data () {
return {
// 省略其他...
roleId: ''
}
}
Guarde el roleId cuando haga clic para asignar permisos
<el-button size="mini" type="success" @click="hAssign(scope.row.id)">分配权限</el-button>
La devolución de llamada correspondiente es:
hAssign(id) {
// 记下来id
this.roleId = id
this.showDialogAssign = true
},
Recibir roleId en subclase
En AssignPerimission.vue, los accesorios de definición complementarios reciben el valor roleId
props: {
roleId: {
type: String,
required: true
}
}
Llamar api para obtener datos
Introducir la api previamente encapsulada
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)
},
Rellenar datos en el-tree
Se han obtenido los datos, ¿cómo llenarlos en el árbol, de modo que se seleccionen algunas casillas de verificación?
Ejemplo: setCheckedKeys + clave de nodo
Sitio web oficial: https://element.eleme.io/#/zh-CN/component/tree#fang-fa
- Agregar clave de nodo de atributo al árbol
<!-- 权限点数据展示 -->
<el-tree
ref="refTree"
:data="permissionData"
:props="{ label: 'name' }"
:default-expand-all="true"
:show-checkbox="true"
:check-strictly="true"
node-key="id"
/>
- llamar a setCheckedKeys
// 获取角色现有的权限
async loadRoleDetail() {
const res = await getRoleDetail(this.roleId)
console.log('获取角色现有的权限', res.data.permIds)
// 回填
this.$refs.refTree.setCheckedKeys(res.data.permIds)
},
resumen
- En el componente el-tree, haga eco de los datos en el componente el-tree a través del método setCheckedKeys
Permisos de asignación de roles: problema de reposición de datos: lo creado solo se ejecuta una vez
razón
Dado que el subcomponente está anidado en el cuadro de diálogo, solo se creará una vez: la creación se ejecuta solo una vez, y las operaciones posteriores de visualización y ocultación no harán que se reconstruya el componente, por lo que: el contenido abierto más tarde es el mismo que la primera vez .
resolver
Solución 1: cuando la capa emergente esté oculta, destruya el subcomponente.
<el-dialog
title="分配角色"
:visible.sync="showDialogRole"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<子组件
+ v-if="showDialogAssign"
/>
</el-dialog>
Ventajas: simple, se obtienen los últimos datos;
Desventajas: La destrucción de componentes tiene ciertos problemas de rendimiento,
Solución 2: haga referencia a los subcomponentes a través de referencias y llame directamente a sus métodos para enviar solicitudes
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (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()
})
}
}
Permisos de asignación de funciones - Guardar configuración
Objetivo
Completar la función de asignación de permisos
tren de pensamiento
Prepare api, llame cuando haga clic en guardar
preparar API
En el archivo src\api\settings.js, agregue una API para asignar permisos
/**
* 给角色分配权限
* @param {*} data {id:角色id, permIds:[] 所有选中的节点的id组成的数组}
* @returns
*/
export function assignPerm(data) {
return request({
url: '/sys/role/assignPrem',
method: 'put',
data
})
}
Llame a api para asignar permisos-análisis
Simplemente llame a la API definida anteriormente y pase los parámetros relevantes.
Hay dos parámetros aquí:
- ¿Cuál es el ID de rol actual?
在点击分配权限时,可以从表格中获取, 父传子
- ¿Cuál es el ID de la lista de permisos correspondiente?
通过el-tree组件的getCheckedKeys来获取用户选中的id列表
Llame a api para asignar permisos - realización de funciones
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([])
}
Finalmente, cuando se cierre la capa emergente, borre los datos seleccionados por el usuario en el árbol
resumen
- el-tree obtiene las claves del nodo actualmente seleccionado: getCheckedKeys
- Para los componentes de el-tree, borre la selección actual: this.$refs.tree.setCheckedKeys([])
Conocer los datos de permisos del usuario
Hasta ahora, hemos implementado todos los aspectos de la idea de diseño de permisos RBAC. Hemos asignado funciones a los empleados y asignado puntos de permiso a las funciones. Los empleados ahora tienen los puntos de permiso correspondientes. A continuación, podemos usar estos puntos de permiso para el control de autoridad real, en el proyecto de recursos humanos, hay dos lugares para el control de autoridad:
- Control de permisos del menú izquierdo (después de que diferentes usuarios ingresan al sistema, los menús que ven son diferentes)
- Control de permisos del botón de operación (botones en la página, diferentes personas tienen diferentes permisos)
donde estan los datos del permiso
Cree nuevos datos de empleado en la administración de empleados y luego use la nueva cuenta de empleado para iniciar sesión (la contraseña es 123456) y vea los datos devueltos de la interfaz de información personal (/api/sys/profile), como se muestra en la figura a continuación, no hay permisos configurados El estado de devolución del , puede ver que los menús y los puntos debajo de los roles están vacíos, y los empleados no tienen permisos en este momento
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-WN9TIr43-1673401451134)(asset/permissionUse/13.png)]
Cómo modificar los datos de permisos
Inicie sesión con una cuenta de administrador y luego asigne dos permisos de menú y un permiso de botón de operación al nuevo empleado recién creado, y luego vuelva a iniciar sesión en la cuenta del empleado para ver información personal y devolver datos
Pasos:
-
Gestión de puntos de permiso > Agregar puntos de permiso de operación de botón en la gestión de empleados
导入,导出
-
Gestión de roles > Director de personal de nuevo rol > Asignar permisos a roles (administración de empleados, importación, exportación)
-
Gestión de empleados > Asignar el rol de director de recursos humanos a los empleados
-
Vuelva a iniciar sesión en la nueva cuenta de empleado, verifique los datos de permiso y observe los elementos data.roles.menus, puntos
Solicitud de permisos-menú izquierdo generado dinámicamente-análisis general
analizar
El inicio de sesión es exitoso, ingrese al protector de navegación:
- Obtener información de permiso personal
- Generar rutas dinámicas accesibles
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y subirla directamente (img-wMfe8si3-1673401451134)(asset/image-20210428122059657.png)]
ejemplo
[Error en la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-L1oHNZ59-1673401451134)(asset/permissionUse/17.png)]
Solicitud de permisos - generar menú izquierdo dinámicamente - método addRoutes
Objetivo
Aprenda addRoutes en el objeto vue-router y utilícelo para agregar dinámicamente configuraciones de enrutamiento
tren de pensamiento
Las páginas (configuración de enrutamiento) a las que pueden acceder los usuarios deben ser dinámicas , por lo que primero debe dominar una API que pueda agregar dinámicamente direcciones de enrutamiento
Uso básico de addRoutes
Formato
router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])
Rol: agregar dinámicamente la configuración de enrutamiento
ejemplo
// 按钮
<button @click="hAddRoute">addRoute</button>
// 回调
hAddRoute() {
this.$router.addRoutes([{
path: '/abc',
component: () => import('@/views/abc'),
}])
},
Efecto
Después de hacer clic en el botón, puede acceder a /abc en la dirección.
código de actualización
-
Elimine la parte del enrutamiento dinámico en la configuración de enrutamiento en router/index.js
const createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), // routes: constantRoutes // 合并动态和静态的路由 , ...asyncRoutes - routes: [...constantRoutes, ...asyncRoutes] + routes: [...constantRoutes] })
-
Introducido en allow.js y agregado dinámicamente usando addRoutes
Transforme la tabla de enrutamiento dinámico que se escribió directamente de forma estática en el enrutador al
addRoutes
formulario agregado por la llamada al método
// 引入所有的动态路由表(未经过筛选)
+ 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()
})
Efecto de aceptación
-
Solo queda la página de inicio estática en el menú de la izquierda (que se resolverá más adelante)
-
La introducción manual de una determinada dirección de enrutamiento dinámico en el navegador aún está disponible, lo que demuestra que en realidad hemos agregado enrutamiento dinámico a nuestro sistema de enrutamiento.
Solicitud de permiso - generar dinámicamente menú izquierdo - reescribir menú guardar ubicación
análisis del problema
Los datos utilizados por la representación del menú actual (src\layout\components\Sidebar\index.vue): this.$router.options.routes
estos datos son fijos y la tabla de enrutamiento que agregamos a través de addRoutes solo existe en la memoria y no cambiaráthis.$router.options.routes
Si queremos que los datos de enrutamiento se reflejen en el menú inmediatamente después de llamar al método addRoutes, debemos pensar en un método adicional. Piénselo. En el desarrollo de Vue, ¿qué tecnología puede garantizar que las funciones receptivas también se puedan modificar dinámicamente? vuex!
Objetivo
guardar datos de menú en vuex
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-4Q4i5Ywp-1673401451134)(activo/imagen-20210428122540218.png)]
Definir los datos del menú de gestión de vuex
- Módulos complementarios.
src/store/modules
Agregue el módulo menu.js a continuación- Definir datos menuList
- Método para modificar datos setMenuList
// 导入静态路由
import {
constantRoutes } from '@/router'
export default {
namespaced: true,
state: {
// 先以静态路由作为菜单数据的初始值
menuList: [...constantRoutes]
},
mutations: {
setMenuList(state, asyncRoutes) {
// 将动态路由和静态路由组合起来
state.menuList = [...constantRoutes, ...asyncRoutes]
}
}
}
Por supuesto, registre este módulo en src/store/index.js
+ import menu from './modules/menu'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
settings,
user,
+ menu
},
getters
})
2. Envíe setMenuList para generar datos de menú completos
Modifique el código en src/permission.js
if (!store.getters.userId) {
await store.dispatch('user/getUserInfo')
// 动态添加可以访问的路由设置
router.addRoutes(asyncRoutes)
// 根据用户实际能访问几个页面来决定从整体8个路由设置
// 中,过滤中出来几个,然后保存到vuex中
store.commit('menu/setMenuList', asyncRoutes)
}
3. La parte de generación del menú se reescribe para usar los datos en vuex
En el archivo src\layout\components\Sidebar\index.vue, modifique
routes() {
// 拿到的是一个完整的包含了静态路由和动态路由的数据结构
// return this.$router.options.routes
return this.$store.state.menu.menuList
}
resumen
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-FQFaCeh7-1673401451135)(activo/imagen-20210525124226388.png)]
Solicitud de permisos: use datos de permisos para filtrar
Objetivo
En el paso anterior conseguimos:
- El enrutamiento dinámico se agrega dinámicamente al sistema de enrutamiento a través de addRoutes
- Guarda la ruta dinámica en el menú de vuex
Sin embargo, no coincidimos con los datos de permisos. Luego, filtramos el menú dinámico a través de los datos de permisos devueltos por la interfaz para asegurarnos de que el menú completo esté relacionado con el permiso del usuario.
filtrar ideas
[Error en la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-JQ1M2Yrk-1673401451135)(activo/imagen-20210525000107003.png)]
El filtrado usa el nombre como identificador y verifica si el nombre de la ruta es consistente con el subíndice
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-c8GBacDW-1673401451135)(asset/image-20210428153438963.png)]
La convención de interfaz del backend es la siguiente:
- Nombre de página: ID de empleado: empleados
- Nombre de la página: ID de permiso: permisos
- Nombre de página: Identificador de estructura organizativa: departamentos
- Nombre de la página: Ajustes ID: ajustes
- Nombre de página: Id. de salario: salarios
- Nombre de la página: Id. de aprobación: aprobaciones
- Nombre de página: Asistencia Logo: asistencias
- Nombre de la página: Seguro Social Logo: social_securitys
Devolver elementos de menú de acciones
Las páginas a las que el usuario puede acceder se obtienen a través de acciones, y solo necesita regresar de la acción.
Modificar store/modules/user.js
y complementar la declaración de devolución.
// 用来获取用户信息的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
},
Obtenga el valor de retorno de la acción en allow.js y filtre
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)
}
Efecto
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-EgzHy9V4-1673401451135)(asset/permissionUse/15.png)]
resumen
- Obtener el valor de retorno de las acciones
asyncRoutes.filter
Corrección de errores al actualizar la página
pregunta
Si refrescamos el navegador, encontraremos que hemos saltado a la página 404
Para la ruta agregada por addRoute, la pantalla será blanca al actualizar
razón
Ahora la página 404 en nuestra configuración de enrutamiento está en el medio en lugar de al final de todas las rutas.
resolver
Simplemente cambie la página 404 al final de la configuración de enrutamiento
el código
-
path:'*'
Eliminar este elemento de las rutas estáticas en rutas/index.js -
Agregado al final en allow.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()
}
Restablecer rutas al cerrar la sesión
pregunta
Después de salir, inicie sesión nuevamente y descubra que el menú es anormal (la consola tiene una salida que dice que la ruta se repite);
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y subirla directamente (img-xfeMibRM-1673401451136)(asset/image-20210525161618731.png)]
razón
La configuración de enrutamiento se router.addRoutes(filterRoutes)
agrega a través de , y cuando cierra la sesión, no se borra, y vuelve a iniciar sesión y la agrega nuevamente, por lo que hay duplicación.
Es necesario restablecer la autoridad de enrutamiento (restaurar el valor predeterminado) y agregarla nuevamente después de iniciar sesión en el futuro; de lo contrario, se agregará repetidamente
resolver
Nuestros router/index.js
archivos **, encontraron un método de ruta de reinicio
// 重置路由
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
Este método es para volver a crear una instancia de la ruta, lo que equivale a cambiar una nueva ruta. El ** anterior ya 加的路由
no existe. Debe llamarlo cuando cierre la sesión**
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 })
}
Solicitud de permisos - Control de nivel de botones - Análisis
Objetivo
Tanto el empleado A como el empleado B pueden acceder a la misma página (tome la gestión de empleados como ejemplo), pero el empleado A puede exportar Excel, pero el empleado B no puede exportar Excel.
tren de pensamiento
Una vez que el usuario inicia sesión correctamente, los permisos de nivel de botón a los que el usuario puede acceder se almacenan en la matriz de puntos. Y estos datos se almacenan en vuex, por lo que se puede acceder a ellos en cualquier parte del proyecto.
- Si el logotipo de un botón aparece en puntos, se puede mostrar
Solicitud de permiso - Control de nivel de botones - Instrucciones personalizadas
Directivas: v-for, v-if…
Instrucciones definidas por el usuario: instrucciones autodefinidas, porque las instrucciones en sí mismas no son suficientes, por lo que debemos definirlas nosotros mismos.
Úselo para el control de permisos a nivel de botón
Revisar directivas personalizadas
formato de registro
// 注册一个全局自定义指令 `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()
}
})
formato de uso
<input v-foucs="'xxxx'" />
Resolver la verificación de permisos a nivel de botón
En main.js, defina la directiva global
// 注册一个全局自定义指令 `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'
}
}
})
usar
<el-button
+ v-allow="'import_employee'"
type="warning"
size="small"
@click="$router.push('/import')"
>导入excel</el-button>
Aquí el : 'import_employee'
es del identificador
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y subirla directamente (img-OHL0NZor-1673401451136)(asset/image-20210428165654008.png)]
Resumen de los puntos clave del proceso de control de autoridad
Escena de negocios
Hay diferentes departamentos funcionales en la empresa, todos los cuales usan el mismo sistema, y las cosas que las personas de diferentes departamentos necesitan para operar en el sistema son diferentes.
Es necesario configurar diferentes permisos según los diferentes roles de los empleados
Idea de diseño de permiso RBAC
[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-Zjgpc0W3-1673401451136)(asset/image-20210427155035187.png)]
Una idea de diseño basada en roles
- Configurar roles para empleados (un empleado puede tener múltiples roles)
- Configurar puntos de permiso para roles (un rol puede tener múltiples puntos de permiso)
Siempre que los empleados tengan un rol, automáticamente tienen todos los puntos de autoridad vinculados al rol.
3. Módulos comerciales correspondientes basados en ideas de diseño de autoridad
- gestión de empleados
- gestión de roles
- Gestión de puntos de permiso
Los empleados obtienen datos de permisos
La interfaz de información del empleado contiene todos los datos de permiso del empleado actual
userInfo:{
roles: {
menus: [], // 菜单权限数据
points: [] // 按钮权限数据
}
}
Usar datos de permisos para realizar un procesamiento de permisos específico
-
Control de permisos de menú
Iniciar sesión > datos de permisos del menú > coincidir con todos los datos de enrutamiento dinámico local > obtener datos de enrutamiento dinámico filtrados de acuerdo con los permisos
- Agregar al sistema de enrutamiento (el componente addRoutes se puede representar de acuerdo con la ID de la ruta )
- Agregado a la representación del menú izquierdo (administración de vuex + v-for transversal)
-
Control de permisos de botones
Iniciar sesión > Datos de permiso del botón > Usar el ID de permiso independiente del botón para buscar en los datos de permiso
directiva personalizada