一、权限菜单的动态生成
权限菜单应该根据当前登录者所拥有的权限显示菜单,以到达权限控制的目录
权限菜单中显示的菜单为当前用户所拥有的权限
权限菜单是根据数据库生成,数据库中有关权限菜单生成的有以下几张表:
账户表(用户表):描述系统账户信息
账户角色表:描述系统中不同账户所拥有的角色
一个账户可以有多个角色,一个角色可以分配给不同的账户。
账户表和角色表是多对多的关系。
角色表:描述系统中角色信息(权限组)
角色权限表:描述一个角色所拥有的权限
一个角色可以有多个权限,一个权限可以分配不同的角色。
角色表和权限表是多对多的关系。
我们需要的数据格式是这样的:
我们的数据库中,模块表的形式是纵向的,现在的这种数据表示的形式是纵向的,最好的方式是模块表进行一次自连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tp6xHGTm-1614334951049)(项目日记.assets/
)]
并且如果要生成菜单,我们在数据库中给它的表示形式是"module_is_menu"为1。
在上一次,我们将当前用户的信息封装到了session中,所以我们不妨通过用户名来查找该用户所拥有的权限,这涉及到以下几个表:
tbl_module 与 tbl_role_module :通过module_id进行关联
tbl_role_module 与 tbl_role :通过role_id进行关联
tbl_account_role 与tbl_role :通过role_id进行关联
通过账户姓名来查询其菜单的sql语句如下:
SELECT
DISTINCT
p.module_id AS p_id,
p.module_name AS p_name,
s.module_id AS s_id,
s.module_name AS s_name,
s.module_url AS s_url
FROM
tbl_module p
INNER JOIN tbl_module s
ON p.module_id=s.parent_module_id
INNER JOIN tbl_role_module rm
ON s.module_id=rm.module_id
INNER JOIN tbl_role r
ON rm.role_id=r.role_id
INNER JOIN tbl_account_role ar
ON r.role_id = ar.role_id
INNER JOIN tbl_account a
ON ar.account_id=a.account_id
WHERE
p.module_is_menu=1 AND
s.module_is_menu=1 AND
a.account_name=#{account_name}
查询admin用户的菜单数据,返回的表为:
现在我们根据这个表,构建一个这个表对应的实体类:
package com.jiazhong.office.model;
/**
* @ClassName: Module
* @Description: TODO 模块实体类,根据sql查询语句建立的实体类
* @Author: JiaShiXi
* @Date: 2021/2/26 12:51
* @Version: 1.0
**/
public class Module {
private Integer p_id; //父模块编号
private String p_name; //父模块名称
private Integer s_id; //子模块编号
private String s_name; //子模块名称
private String s_url; //子模块url
public Integer getP_id() {
return p_id;
}
public void setP_id(Integer p_id) {
this.p_id = p_id;
}
public String getP_name() {
return p_name;
}
public void setP_name(String p_name) {
this.p_name = p_name;
}
public Integer getS_id() {
return s_id;
}
public void setS_id(Integer s_id) {
this.s_id = s_id;
}
public String getS_name() {
return s_name;
}
public void setS_name(String s_name) {
this.s_name = s_name;
}
public String getS_url() {
return s_url;
}
public void setS_url(String s_url) {
this.s_url = s_url;
}
@Override
public String toString() {
return "Module{" +
"p_id=" + p_id +
", p_name='" + p_name + '\'' +
", s_id=" + s_id +
", s_name='" + s_name + '\'' +
", s_url='" + s_url + '\'' +
'}';
}
}
接下来,编写Dao层数据:
package com.jiazhong.office.dao.rbac;
import com.jiazhong.office.model.Module;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @InterfaceName: MainMenuDao
* @Description: TODO 主菜单生成Dao
* @Author: JiaShiXi
* @Date: 2021/2/26 12:49
* @Version: 1.0
**/
@Repository
public interface ModuleDao {
/**
* 通过账户名查询模块集合
* @return
*/
public List<Module> getModuleListByAccountName(@Param("account_name") String account_name);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jiazhong.office.dao.rbac.ModuleDao">
<select id="getModuleListByAccountName" resultType="com.jiazhong.office.model.Module">
SELECT
DISTINCT
p.module_id AS p_id,
p.module_name AS p_name,
s.module_id AS s_id,
s.module_name AS s_name,
s.module_url AS s_url
FROM
tbl_module p
INNER JOIN tbl_module s
ON p.module_id=s.parent_module_id
INNER JOIN tbl_role_module rm
ON s.module_id=rm.module_id
INNER JOIN tbl_role r
ON rm.role_id=r.role_id
INNER JOIN tbl_account_role ar
ON r.role_id = ar.role_id
INNER JOIN tbl_account a
ON ar.account_id=a.account_id
WHERE
p.module_is_menu=1 AND
s.module_is_menu=1 AND
a.account_name=#{account_name}
</select>
</mapper>
编写菜单视图模型:
package com.jiazhong.office.model.view;
import java.util.List;
/**
* @ClassName: MenuView
* @Description: TODO 菜单视图实体类
* @Author: JiaShiXi
* @Date: 2021/2/26 12:42
* @Version: 1.0
**/
public class MenuView {
private Integer menuId; //菜单编号
private String menuName; //菜单名称
private String menuUrl; //菜单路径
private List<MenuView> subMenuList; //子菜单项
public Integer getMenuId() {
return menuId;
}
public void setMenuId(Integer menuId) {
this.menuId = menuId;
}
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public String getMenuUrl() {
return menuUrl;
}
public void setMenuUrl(String menuUrl) {
this.menuUrl = menuUrl;
}
public List<MenuView> getSubMenuList() {
return subMenuList;
}
public void setSubMenuList(List<MenuView> subMenuList) {
this.subMenuList = subMenuList;
}
@Override
public String toString() {
return "MenuView{" +
"menuId=" + menuId +
", menuName='" + menuName + '\'' +
", menuUrl='" + menuUrl + '\'' +
", subMenu=" + subMenuList +
'}';
}
}
编写sevice层数据(接口省略,这里只写其实现类):
package com.jiazhong.office.service.rbac.impl;
import com.jiazhong.office.dao.rbac.ModuleDao;
import com.jiazhong.office.model.Module;
import com.jiazhong.office.model.view.MenuView;
import com.jiazhong.office.service.rbac.MainMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: MainMenuServiceImpl
* @Description: TODO 主菜单生成服务类
* @Author: JiaShiXi
* @Date: 2021/2/26 12:46
* @Version: 1.0
**/
@Transactional
@Service("mainMenuService")
public class MainMenuServiceImpl implements MainMenuService {
@Autowired
private ModuleDao moduleDao;
@Override
public List<MenuView> getMenuList(String account_name) {
//获取从数据库中查到的模块集合
List<Module> moduleList = moduleDao.getModuleListByAccountName(account_name);
System.out.println("moduleDao"+moduleList);
//创建主菜单集合
List<MenuView> menuViewList = new ArrayList<>();
//声明一个主菜单变量
MenuView menuView = null;
for (Module module : moduleList) {
/**
* 如果是第一次循环,menuView == null,创建menuView对象
* 如果循环>1,menuView!=null,但是menuView中的menuId与module中的父ID不一致,将一个新的MenuView对象赋给menuView
*/
if (menuView == null || !menuView.getMenuId().equals(module.getP_id())){
//创建一个主菜单对象赋给主菜单变量
menuView = new MenuView();
//设置主菜单的相关属性(主菜单的属性不论循环多少次只需要设置一次,所以只能在创建主菜单对象时设置)
menuView.setMenuId(module.getP_id());
menuView.setMenuName(module.getP_name());
//设置主菜单的子菜单集合(此时子菜单还不确定,所以先new一个空集合)
menuView.setSubMenuList(new ArrayList<>());
//添加到主菜单集合中
menuViewList.add(menuView);
}
//创建一个子菜单
MenuView subMenu = new MenuView();
//设置子菜单的属性(子菜单每循环一次就需要设置一次)
subMenu.setMenuId(module.getS_id());
subMenu.setMenuName(module.getS_name());
subMenu.setMenuUrl(module.getS_url());
//将子菜单加入到父菜单中的子菜单集合
menuView.getSubMenuList().add(subMenu);
}
System.out.println("MainMenuService"+menuViewList);
return menuViewList;
}
}
编写controller层:
package com.jiazhong.office.controller.rbac;
import com.jiazhong.office.commons.Constant;
import com.jiazhong.office.commons.CurAccount;
import com.jiazhong.office.model.view.MenuView;
import com.jiazhong.office.service.rbac.MainMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.List;
/**
* @ClassName: MainMenuController
* @Description: TODO 主菜单生成控制器
* @Author: JiaShiXi
* @Date: 2021/2/26 12:41
* @Version: 1.0
**/
@RestController
@RequestMapping("/main")
public class MainMenuController {
@Autowired
private MainMenuService mainMenuService;
/**
* 获取主菜单集合
* @param session 从session对象中获取存入的账户视图对象
* @return
*/
@GetMapping("/getMenuList")
public List<MenuView> getMenuList(HttpSession session){
//从session对象中获取存入的账户视图对象
CurAccount curAccount = (CurAccount) session.getAttribute(Constant.SESSION_CUR_ACCOUNT);
//获取账户名
String account_name = curAccount.getAccount_name();
//从服务层获取主菜单集合
List<MenuView> menuList = mainMenuService.getMenuList(account_name);
System.out.println("MainMenuController"+menuList);
return menuList;
}
}
这里的有关于后端的权限菜单的数据模型就生成好了,现在我们来写一下前端:
最后,测试,admin用户的权限菜单:
测试成功!
这里需要注意的是,要将数据库tbl_module中的module_url的值改为路由配置url的值!