一、需求
不同用户拥有不同角色,不同角色拥有不同权限。用户登录之后,要根据用户的不同角色和不同权限,来动态调整侧边栏的展示。
二、数据库设计
实现功能至少需要这几个表格:
1、用户信息表(记录UUID或者唯一标示位);
2、角色表(定义角色唯一ID和角色名称);
Parent);
4、用户—角色表(不同用户对应不同角色);
5、角色—权限表(不同的角色对应不同的权限)。
三、代码实现思路
1、前端--->后台(标示用户的唯一ID);
2、通过唯一ID去“用户—角色表”查询到对应的Role;
3、通过对应的Role去“角色—权限表”找到与之对应的所有Permissions;
转换为父子级关系的树型结构;
5、将树结构--->前端;
6、Vue获取树结构,渲染侧边栏。
四、后台实现代码示例(仅关键代码)
Controller:
@GetMapping
public Result getAllPermissionByRole(@RequestParam("gh") String gh){
String roleName = userToRoleService.getRoleByHdgh(gh);
if(!roleName.isEmpty()){
List<PermissionDTO> permissionDTOS = permissionService.getAllPermissionByRole(roleName);
if(!permissionDTOS.isEmpty()){
return new Result(Code.SUCCESS, permissionDTOS, "OK");
}
else {
return new Result(Code.ERROR, null, "Error");
}
}
return new Result(Code.FAILED, null, "Failed");
}
其中:
role是通过查询用户—角色表,得到的RoleName(此处根据自己业务实现);
UserToRoleService是用户对应权限表的service;
PermissionDTO是包含父子级关系的Permission类;
Result是个人定义的返回结果类。
PermissionDTO:
@Data
public class PermissionDTO {
// 权限ID
private Integer permissionId;
// 权限代码
private String permissionCode;
// 权限名称
private String permissionName;
// 权限全部代码
private String permissionCodeAll;
// 权限路径
private String permissionPath;
// 父级
private Integer permissionParent;
// 子节点
private List<PermissionDTO> children = new ArrayList<>();
/*
* 排序,根据permissionId排序
* 升序
* */
public static Comparator<PermissionDTO> order(){
Comparator<PermissionDTO> comparator = (o1, o2) -> {
if(o1.getPermissionId() != o2.getPermissionId()){
return (int)(o1.getPermissionId() - o2.getPermissionId());
}
return 0;
};
return comparator;
}
}
Permission:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
// 权限ID,主键
@TableId
private Integer permissionId;
// 权限代码
private String permissionCode;
// 权限名称
private String permissionName;
// 权限全部代码
private String permissionCodeAll;
// 权限路径
private String permissionPath;
// 父级
private Integer permissionParent;
// 备注
private String bz;
}
PermissionServiceImpl:(获取全部权限的实现类)
public List<PermissionDTO> getAllPermissionByRole(String role) {
// 先获取与role对应的所有Permissions
List<Permission> permissions = permissionDao.getAllPermissionByRole(role);
// 将获取的Permissions 转换成 PermissionDTO类型(包含children的那种)
if(!CollectionUtils.isEmpty(permissions)){
List<PermissionDTO> permissionDTOS = new ArrayList<>();
permissionDTOS = PermissionConverter.permission2permissionDTO(permissions);
// 转换为PermissionDTO树类型
return PermissionTreeBuilder.build(permissionDTOS);
}
return null;
}
此处涉及两个方法:
1、permission2permissionDTO(),这是一个转换类,将没有children的Permission转换为带有children的PermissionDTO,方便生成树结构。
2、PermissionTreeBuilder(),这是一个通过递归方法,将PermissionDTO生成一个树结构的List。
permission2permissionDTO:
public static List<PermissionDTO> permission2permissionDTO(List<Permission> permissions){
List<PermissionDTO> permissionDTOS = new ArrayList<>();
if(!CollectionUtils.isEmpty(permissions)){
for(Permission permission:permissions){
if(!Objects.equals(permission.getPermissionPath(), "null")){
PermissionDTO permissionDTO = new PermissionDTO();
BeanUtils.copyProperties(permission, permissionDTO);
permissionDTOS.add(permissionDTO);
}
}
}
return permissionDTOS;
}
PermissionTreeBuilder:
public class PermissionTreeBuilder {
/**
* 构建多级菜单树
* @param nodes
* @return List<PermissionDTO>
*/
public static List<PermissionDTO> build(List<PermissionDTO> nodes){
// 根节点
List<PermissionDTO> rootMenu = new ArrayList<>();
for(PermissionDTO permissionDTO:nodes){
if(permissionDTO.getPermissionParent() == 0){
rootMenu.add(permissionDTO);
}
}
// 根据PermissionDTO类的order进行从小到大排序
Collections.sort(rootMenu, PermissionDTO.order());
// 为根节点设置子节点,getChild递归调用
for(PermissionDTO permissionDTO:rootMenu){
// 获取根节点下的所有子节点 使用下面getChild方法
List<PermissionDTO> childList = getChild(permissionDTO.getPermissionId(), nodes);
// 给根节点设置子节点
permissionDTO.setChildren(childList);
}
return rootMenu;
}
/**
* 获取子目录节点
* @param id
* @param nodes
* @return List<PermissionDTO>
*/
private static List<PermissionDTO> getChild(Integer id, List<PermissionDTO> nodes){
// 子菜单
List<PermissionDTO> childList = new ArrayList<>();
for(PermissionDTO permissionDTO:nodes){
// 遍历所有节点,将所有菜单的父ID与传过来的根节点ID比较,相等说明为该根节点的字节点
if(permissionDTO.getPermissionParent().equals(id)){
childList.add(permissionDTO);
}
}
// 递归
for(PermissionDTO permissionDTO:childList){
permissionDTO.setChildren(getChild(permissionDTO.getPermissionId(), nodes));
}
// 根据所有PermissionDTO类的order进行从小到大排序
Collections.sort(childList, PermissionDTO.order());
// 如果节点下面没有子节点,返回一个空的List
if(childList.size() == 0){
return new ArrayList<>();
}
return childList;
}
}
至此,后台部分宣告完毕。
五、 Vue 2.x实现示例
创建一个component:
<template>
<div>
<template v-for="item in this.menuList">
<el-submenu :index="item.permissionId+''" v-if="item.children.length>0" :key="item.permissionId+''">
<template slot="title" style="padding-left:10px">
<span slot="title">{{item.permissionName}}</span>
</template>
<MenuTree :menuList="item.children"></MenuTree>
</el-submenu>
<el-menu-item
v-else
:index="item.permissionPath+''"
:route="item.permissionPath"
@click="savePath(item.permissionPath)"
:key="item.permissionId+''"
style="padding-left: 50px;"
>
<span>{{item.permissionName}}</span>
</el-menu-item>
</template>
</div>
</template>
<script>
export default {
name: "MenuTree",
beforeMount() {},
props: ["menuList"],
methods:{
savePath(path){
localStorage.setItem("activePath", path);
this.activePath = path;
}
},
created(){
}
}
</script>
<style scoped>
</style>
将此组件,插入到页面侧边栏的布局之中,
<MenuTree :menuList="this.menuList"></MenuTree>
在此处需要往组件里传入menuList值,可以通过axios请求来获取此值,当然此处的用户ID需要自己根据实际情况来获取。
axios发送get请求获取menuList(仅供参考):
getMenu(){
axios.get("/permissions",{
params:{
gh:localStorage.getItem("gh")
}
}).then((res)=>{
// 根据自己封装后台的返回值
this.menuList = res.data.data
})
}
至此基本就可以实现根据用户角色,来展示出不同的侧边栏,实现前端的权限控制。