This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!
preface
Before implementing dynamic routing and permission control, consider the following three concepts:
What is dynamic routing?
Most projects today are front and back separate, which results in the front-end taking control of the routing hops that SpringMVC used to do on the back end. Dynamic routing is a route list generated by the front-end based on the routing information returned by the back-end
What is permission control?
Permission control means that after a user initiates a request to invoke an interface, the user determines whether the user has the corresponding permission, and then permits or intercepts the request
What is the relationship between dynamic routing and permission control?
Requests initiated by users are all initiated from the pages pointed to by a certain route. Therefore, the relationship between permissions and routes is many-to-one. That is, a route has multiple permissions.
For example, there are query and edit operations in the list page /list. When user A initiates A request to query the list, the back end can determine whether user A has the query permission in the list page /list.
Past the link
- Brief account main function introduction
- Brief introduction and deployment of the accounting backend environment
- Brief introduction and deployment of account front end environment
- Solve the small program scan code authorization prompt Token cannot be empty
- Database design
- Resolve AOP logging errors
- Brief Spring Security
- Integrated Spring Security
First, thinking analysis
The realization is mainly divided into two parts:
- Back-end generation Generates route permission information based on user roles
- The front-end generates a routing information table based on the returned routing permission information
The routing permission information returned by the backend in the account is as follows:
Because the front end needs to build the permission routing table based on the data in the tree structure, the back end needs to return the obtained list data to the front end as a tree structure
Tips: Modify the AntDesginPro route guard I really changed a long time!
Second, the implementation scheme
Data table design
The routing permission table mainly contains two information: the routing address and the permission character
The routing permission table is designed as follows (TB_menu) :
Route permission information is generated on the backend
The Controller layer
@LoginRequired
@apioperation (value = "get user's role and menu ")
@GetMapping("/roleMenus")
publicResult<? > getRoleMenus() {// User role
List<RoleDO> roleDOS = roleService.getByUserId(LocalUserId.get());
// User menu permissions
List<MenuDO> menuDOS = menuService.getUserMenus(LocalUserId.get());
List<MenuBO> menuBOS = menuService.copyFromMenuDos(menuDOS);
List<MenuBO> tree = menuService.generatorMenuTree(menuBOS);
RoleMenuVO roleMenuVO = new RoleMenuVO(LocalUser.get(), roleDOS, tree);
return Result.success(roleMenuVO);
}
Copy the code
The Service layer converts the list to a Tree
@Override
public List<MenuBO> generatorMenuTree(List<MenuBO> boList) {
return generatorTree(boList);
}
private List<MenuBO> generatorTree(List<MenuBO> voList) {
List<MenuBO> tree = list2Tree(voList, null);
sortTree(tree);
return tree;
}
/** * list to tree */
private List<MenuBO> list2Tree(List<MenuBO> list, Integer pId) {
List<MenuBO> tree = new ArrayList<>();
Iterator<MenuBO> it = list.iterator();
while (it.hasNext()) {
MenuBO m = it.next();
if (m.getParentId() == pId) {
tree.add(m);
// Delete the added elementit.remove(); }}// Look for child elements
tree.forEach(n -> n.setChildren(list2Tree(list, n.getId())));
return tree;
}
Copy the code
The front end generates the permission route list
Generate the main code for dynamic routing
Note: in this case, the identity of the permission is placed in the meta of the route
/** * dynamically generate menu *@returns {Promise<Router>}* /
export const generatorDynamicRouter = (ret) = > {
return new Promise((resolve, reject) = > {
const menuNav = []
rootRouter.children = ret
menuNav.push(rootRouter)
const routers = generator(menuNav)
routers.push(notFoundRouter)
resolve(routers)
})
}
/** * Format tree structure data to generate vue-router-level routing table **@param routerMap
* @param parent
* @returns {*}* /
export const generator = (routerMap, parent) = > {
const children = []
routerMap.forEach(item= > {
// If it is a button, the loop is terminated
if(item.menuType ! = ='F') {
// If it is a menu, find all permissions of the child node and form an array
if (item.menuType === 'C' && item.children) {
const arr = findMenuPermissions(item.children)
if (arr.length > 0) {
item.permissionSign = arr
}
}
const path = item.outerChain ? `${item.path}` : `${parent && parent.path ! = ='/' && parent.path || ' '}/${item.path === '/' ? ' ' : item.path}`
// Check whether it is a directory
if (item.menuType === 'M') {
item.component = 'RouteView'
}
const currentRouter = {
// Dynamic routing address splicing
path: path,
// Route name
name: item.menuName,
// Dynamically load the components of the page corresponding to the route
component: item.outerChain ? undefined : (constantRouterComponents[item.component]) || (() = > import(`@/views${parent && parent.path ! = ='/' && parent.path || ' '}/${item.component}`)),
meta: {
title: item.menuTitle,
icon: item.iconName || undefined.hiddenHeaderContent: false.target: item.outerChain ? '_blank' : undefined.permission: item.permissionSign
}
}
/ / redirection
// item.redirect && (currentRouter.redirect = item.redirect)
// Change the redirect of the parent node if the current node is home
if (currentRouter.path === '/home') {
parent.redirect = currentRouter.path
}
if (item.outerChain === 1) {
currentRouter.redirect = item.path
}
// Whether there is a submenu, and recursively processing
if (item.children && item.children.length > 0) {
// Recursion
const t = generator(item.children, currentRouter)
// If there are no children, no value is assigned
if (t.length > 0) currentRouter.children = t
}
children.push(currentRouter)
}
})
return children
}
Copy the code
The front end determines whether a button has permission
/** * use v-action:[method] on components that need to control Action level permissions, as follows: * <i-button v-action:add > Add user </a-button> * <a-button v-action:delete> Delete user </a-button> * <a v-action:edit@click="edit(record)"> </a> * * - Components that use this command will be hidden when the current user does not have permissions * - When background permissions are different from the mode provided by pro, just modify the permissions filter here * * tips: This has been modified according to the background return value */
const action = Vue.directive('action', {
inserted: function (el, binding, vnode) {
const actionName = binding.arg
// The current user menu list, including permissions
const permissions = vnode.context.$route.meta.permission
let hasPermission = false
for (let i = 0; i < permissions.length; i++) {
if (permissions[i] && permissions[i] === actionName) {
hasPermission = true
break}}if(! hasPermission) { el.parentNode && el.parentNode.removeChild(el) || (el.style.display ='none')}}})Copy the code
The results of
The administrator user’s menu route is as follows:
The menu route for common users is as follows:
test
This assumes that the front end forgot to hide the edit button with the permission character (normally, this button will not be displayed if you do not have the edit permission).
The tester does not have permission to create new users
When you click ok to add a user, a message is displayed indicating that the permission is insufficient, indicating that the function is normal
Third, summary
The above code can be found in the notebook back end and notebook PC end, if you are interested in the warehouse to check.
Thanks for seeing the end, it was a great honor to help you ~❤❤❤❤