一、需求

        不同用户拥有不同角色,不同角色拥有不同权限。用户登录之后,要根据用户的不同角色和不同权限,来动态调整侧边栏的展示。

二、数据库设计

        实现功能至少需要这几个表格:

        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
      })
    }

至此基本就可以实现根据用户角色,来展示出不同的侧边栏,实现前端的权限控制。