RABC authority design idea
In order to see different pages and perform different functions after different accounts (employees and presidents) log in to the system, RABC(Role-Based Access control) permission model is to allocate visual pages according to the permissions of roles.
Three key points:
User: the person who uses the system
Role: what is the position of the person using the system (employee, manager, President)
Permission point: what the position can do (function module in the left menu bar - > add, delete, modify and query)
Test process:
① Add an employee on the employee management page. This is the user of the three elements
② Assign roles to new employees
③ Assign permissions to roles in company settings
💢 Permissions in the system cannot be added at will. They must be developed permissions (pages that can be realized in the menu bar on the left)
💢 There is a one to many relationship between users and roles, and one person has several roles.
Concrete implementation
1. Assign roles
Click Assign role and pop-up box, which contains a list of existing roles. When you click Assign role, pass the id to display the existing roles of the current user according to the id.
Assign role parent component Src / employees / employee vue
<template slot-scope="scope"> <el-button type="text" size="smell" @click="assignFn(scope.row)">Assign roles</el-button> </template> <el-dialog title="Assign roles" :visible.sync="showDialogRole" :close-on-press-escape="false" :close-on-click-modal="false" @close="showDialogRole=false" > <assign-role :id="curId" ref="assignRole" @close="showDialogRole=false" /> </el-dialog> //Import import recovery box subcomponent // -------------------------------------------Assign roles---------------------------------- assignFn(row) { this.showDialogRole = true this.curId = row.id this.$nextTick(() => { this.$refs.assignRole.getRoleListFn() }) }
Sub component of assigned role: employees / assignrole vue
<template> <div> <el-checkbox-group v-model="rolesList"> <el-checkbox v-for="item in checkList" :key="item.id" :label="item.id">{{ item.name }}</el-checkbox> </el-checkbox-group> <div style="margin-top: 20px; text-align: right"> <el-button type="primary" @click="submitFn">determine</el-button> <el-button @click="closeDialog">cancel</el-button> </div> </div> </template> <script> import { getAllRoleAPI } from '@/api/settings' import { getDetailInfo } from '@/api/user.js' import { assignRolesAPI } from '@/api/employees.js' export default { name: 'AssignRole', props: { id: { type: String, required: true } }, data() { return { checkList: [], // Role list rolesList: []// The user already has a role } }, created() { }, methods: { // ------------------------------------Submit role----------------------------------- async submitFn() { const resp = await assignRolesAPI({ id: this.id, roleIds: this.rolesList }) console.log(resp) this.$emit('close') }, // ----------------------------------Get role list---------------------------------- async getRoleListFn() { const resp = await getAllRoleAPI({ page: 1, pagesize: 100 }) console.log(resp) this.checkList = resp.data.rows const res = await getDetailInfo(this.id) console.log(res) this.rolesList = res.data.roleIds }, // -------------------------------------Cancel button------------------------------------ closeDialog() { this.$emit('close') } } } </script>
💢 In El checkbox group v-model = "rolesList", the bound value of v-model is an array, indicating that multiple choices can be made.
💢 When rendering data in a template
{{ item.name }}
Where label determines the currently selected value, {{role name to be displayed}}
2. Assign permissions
In parent component (Views / settings / settings. Vue): prepare pop-up - > register event - > provide data method
<template> <div class="settings-container"> <div class="app-container"> <el-card> <!-- Specific page structure --> <el-tabs> <!-- Placement tab --> <el-tab-pane label="Role management"> <!-- form --> <el-table :data="tableList"> <el-table-column label="operation"> <!-- scope It's just the name of the slot. What's important is the name in it.row This is the object of each line, which is a fixed writing method --> <template slot-scope="scope"> <el-button size="small" type="success" @click="hAssign(scope.row.id)">Assign permissions</el-button> </template> </el-table-column> </el-table> <el-row type="flex" justify="center" align="middle" style="height: 60px"> </el-row> </el-tab-pane> </el-tabs> </el-card> <!-- Bomb layer for assigning permissions --> <el-dialog title="Assign permissions(The first level is the view permission of the routing page-The second level is button operation authority)" :visible.sync="showDialogAssign" > <assign-permission ref="assignPermission" :role-id="roleId" @close="showDialogAssign=false" /> </el-dialog> </div> </div> </template> <script> export default { name: 'Setting', components: { assignPermission }, data() { return { showDialogAssign: false, // Assign permissions dialog box methods: { // -----------------------------------------Assign permissions------------------------------------- hAssign(id) { this.roleId = id this.showDialogAssign = true this.$nextTick(() => { this.$refs.assignPermission.getRoleDetail() }) } } } </script>
In the sub component (settings/assignPermission.vue):
<template> <div> <!-- Permission point data display:check-strictly set up true,You can turn off parent-child associations --> <el-tree ref="tree" :data="permissionData" :props="{ label: 'name' }" node-key="id" default-expand-all :show-checkbox="true" :check-strictly="true" /> <div style="text-align:right;"> <el-button @click="hCancel">cancel</el-button> <el-button type="primary" @click="getAssignRoleFn">determine</el-button> </div> </div> </template> <script> import { getPermissionListAPI } from '@/api/permissions.js' import { tranListToTreeData } from '@/utils/index.js' import { getRoleDetail, getAssignRoleAPI } from '@/api/settings.js' export default { props: { roleId: { type: String, required: true } }, data() { return { permissionData: [] // Store permission data } }, created() { this.getPermissionListFn() }, methods: { // -------------------------------------------Get permission list------------------------------------- async getPermissionListFn() { const resp = await getPermissionListAPI() console.log(resp) this.permissionData = tranListToTreeData(resp.data) console.log('Array to tree', this.permissionData) }, // --------------------------------------------Get role details------------------------------------ async getRoleDetail() { const resp = await getRoleDetail(this.roleId) console.log(resp) // Backfill to tree this.$refs.tree.setCheckedKeys(resp.data.permIds) }, // ---------------------------------------------Close the bomb layer------------------------------------------ hCancel() { // Close the spring layer through the parent component this.$emit('close') // The next time you get the role permission array based on id, empty the container to avoid affecting the next save this.$refs.tree.setCheckedKeys([]) }, // ------------------------------------------Assign permissions to roles-------------------------------------- async getAssignRoleFn() { const pid = this.$refs.tree.getCheckedKeys() const resp = await getAssignRoleAPI({ id: this.roleId, permIds: pid }) console.log(resp) this.hCancel()// Notify parent component to close bomb layer this.$message.success('Allocation succeeded') } } } </script> <style> </style>
3. Page permission control
1. Menu permission control on the left (different users will see different menus after entering the system) 2. Operation button permission control (Different people have different permissions for the buttons on the page) 3. Location of permission data: the following figure shows the permissions that administrators can see when logging in.![Insert picture description here](https://img-blog.csdnimg.cn/5bb6a3a086d6498c911224f19bc2840e.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YuH5pWi54mb54mb77yM5Yay5Yay5Yay,size_11,color_FFFFFF,t_70,g_se,x_16)3.1 modify permission data
Only administrators can modify permission data, so first add users - > assign roles - > assign permissions, and re observe permission data (data.roles.menu,
points)
3.2 dynamically generate the left menu
The new user logs in successfully, and the page jumps and enters the navigation guard
3.3 in router / index In the routing configuration in JS, the part that deletes dynamic routing is changed to: routes: [... constantRoutes]
3.4 in permission JS and add dynamically with addRoutes. At this time, only the static home page is left for the dynamic route on the left. You can enter the address in the address bar to jump (the role of addRoutes)
3.5 returning menu items from actions
async getUserInfo(context) { // 1. ajax obtains basic information, including user id const rs = await getUserInfoApi() console.log('Used to obtain user information,', rs) // 2. Resend the request according to the user id (rs.data.userId) to obtain the details (including the avatar) const info = await getUserDetailById(rs.data.userId) console.log('Get details', info.data) // Merge the two copies obtained above and save them to vuex context.commit('setUserInfo', { ...info.data, ...rs.data }) // The current user can see the menu res.data roles. menus + return rs.data.roles.menus },
3.6 in permission Get the return value of action in JS and filter it
/ Introduce all dynamic routing tables(Unfiltered) + import router, { asyncRoutes } from '@/router' const whiteList = ['/login', '/404'] router.beforeEach(async(to, from, next) => { // Open progress bar NProgress.start() // Get local token global getter const token = store.getters.token if (token) { // There is a token if (to.path === '/login') { next('/') } else { if (!store.getters.userId) { + const menus = await store.dispatch('user/getUserInfo') //Filter dynamic arrays by permissions + const filterRoutes = asyncRoutes.filter(route => { + const routeName = route.children[0].name + return menus.includes(routeName) + }) // 1. Rewrite to dynamic addition + router.addRoutes(filterRoutes) //2. When generating the left menu, you should also get it from vuex + store.commit('menu/setMenuList', filterRoutes) + //3. Solve the white screen bug during refresh next({ ...to, // Ensure that the route is added before entering the page (which can be understood as entering again) replace: true// Go back again without retaining the duplicate history }) }else{ next() } } else { // No token if (whiteList.includes(to.path)) { next() } else { next('/login') } } // End progress bar NProgress.done() })
💢Current menu(src\layout\components\Sidebar\index.vue)Data used: this.$router.options.routes **You can get the current routing configuration and the set routing table data**But this data is fixed, so replace this data with this.$router.options.routes You can get the data of the routing table dynamically.
💢 If you want to display the routing table data in the left menu bar immediately after calling the addRoutes method, save the dynamic routing menu in vuex
3.7 fixing bug s
3.7. 1 solve the problem of refreshing the white screen (/ / 3...)
3.7. 2 after exiting, log in again and find that the menu is abnormal (the console has an output saying that the route is repeated)
Reason: route setting is through router Addroutes (filterroutes) is added. When exiting, it is not cleared. Log in again and add it again, so it is repeated.
You need to reset the routing permission (restore the default) and add it again after logging in in the future. Otherwise, it will be added repeatedly
solve:
router/index.js file, there is a reset routing method
// Reset route export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // Reset the matching path of the route }
When logging out, you can call store / modules / user js
import { resetRouter } from '@/router' // Action action to exit logout(context) { // 1. Remove vuex personal information context.commit('removeUserInfo') // 2. Remove token information context.commit('removeToken') // 3. Reset route + resetRouter() }
4. Button level control
4.1 user defined instructions: self defined instructions. Because the instructions are not enough, we need to define them ourselves.
4.2 solve button level permission verification - in main JS, define global instructions
// Register a global custom directive ` v-allow` Vue.directive('allow', { inserted: function(el, binding) { // Take points from vuex, const points = store.state.user.userInfo.roles.points // If points has binding Value is displayed if (points.includes(binding.value)) { // console.log('judge whether this element will be displayed ', el, binding.value) } else { // el.style.display = 'none', this is only hidden. Those who understand the business can also be displayed through inspection, so they should be destroyed el.parentNode.removeChild(el) } } })
use
<el-button + v-allow="'import_employee'" type="warning" size="small" @click="$router.push('/import')" >Import excel</el-button>
※ here: 'import_ 'employee' comes from an identifier