介绍
代码部分为自己手写,逻辑简单,具体逻辑还需根据公司具体业务,整体思路逻辑是大部分公司通用的。
建库
角色权限控制,基本开发都是下面三张表,可以直接拷贝。
- menu 权限表
CREATE TABLE `menu` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`menu_name` varchar(128) DEFAULT NULL COMMENT '菜单名称',
`level` tinyint(4) DEFAULT NULL COMMENT '菜单级别',
`pid` int(11) DEFAULT NULL COMMENT '父菜单ID',
`create_id` int(11) DEFAULT NULL COMMENT '创建人ID',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_id` int(11) DEFAULT NULL COMMENT '修改人',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`state` tinyint(4) DEFAULT NULL COMMENT '删除标记 0 未删除 1 已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
- user_role 角色表
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '角色名称',
`create_id` int(11) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_id` int(11) DEFAULT NULL COMMENT '修改人',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`state` tinyint(4) DEFAULT NULL COMMENT '删除标记 0 未删除 1 已删除',
PRIMARY KEY (`Id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
- menu_role_ref 角色权限中间表
CREATE TABLE `menu_role_ref` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`menu_id` int(11) DEFAULT NULL COMMENT '菜单ID',
`user_role_id` int(11) DEFAULT NULL COMMENT '用户角色ID',
`create_id` int(11) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_id` int(11) DEFAULT NULL COMMENT '修改人',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
接口部分
为了方便理解,我会写出完整逻辑过程,从controller -> service -> mapper
1. 权限列表树形菜单
controller:
/**
* 权限列表树形菜单
*
* @return
*/
@GetMapping("/permission/list")
public ResultEntity menuList() {
return menuService.menuList();
}
service:
@Override
public ResultEntity menuList() {
ResultEntity result = new ResultEntity();
List<Menu> menus = menuMapper.selectAll();
if (CollectionUtils.isEmpty(menus)) {
result.setCode(4000);
result.setMsg("数据库数据为空");
return result;
}
Map<Integer, List<Menu>> pidMap = menus.stream().collect(Collectors.groupingBy(Menu::getPid));
Menu menu = new Menu();
// 递归调用填充数据
addMenuByPid(menu, pidMap);
result.setCode(200);
result.setMsg("查询成功");
result.setData(menu.getChildren());
return result;
}
/**
* 将菜单分级嵌套组合
*
*/
private void addMenuByPid(Menu menu, Map<Integer, List<Menu>> pidMap) {
if (Objects.isNull(menu.getId())) { // 一级菜单
menu.setChildren(pidMap.get(0));
} else {
// 获取当前菜单的子菜单
List<Menu> childMenu = pidMap.get(menu.getId());
// 将子菜单插入当前菜单
menu.setChildren(childMenu);
}
if (!CollectionUtils.isEmpty(menu.getChildren())) { // 子菜单为空时跳出递归
// 当前菜单下填充子菜单
menu.getChildren().forEach(m -> addMenuByPid(m, pidMap));
}
}
mapper:
<select id="selectAll" resultType="com.zlp.demo.entity.Menu">
SELECT id, menu_name, level, pid, create_id, create_time, update_id, update_time, state
FROM menu
WHERE state = 0
</select>
接口返回结果:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"menuName": "一级菜单1",
"level": 1,
"pid": 0,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:03:56.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:04:01.000+00:00",
"children": [
{
"id": 3,
"menuName": "二级菜单11",
"level": 2,
"pid": 1,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:05:05.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:05:09.000+00:00",
"children": [
{
"id": 7,
"menuName": "三级菜单31",
"level": 3,
"pid": 3,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:07:01.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:07:10.000+00:00",
"children": null
},
{
"id": 8,
"menuName": "三级菜单32",
"level": 3,
"pid": 3,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T08:00:56.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T08:00:56.000+00:00",
"children": null
}
]
},
{
"id": 4,
"menuName": "二级菜单12",
"level": 2,
"pid": 1,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:05:34.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:05:38.000+00:00",
"children": null
}
]
},
{
"id": 2,
"menuName": "一级菜单2",
"level": 1,
"pid": 0,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:04:26.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:04:30.000+00:00",
"children": [
{
"id": 5,
"menuName": "二级菜单21",
"level": 2,
"pid": 2,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:06:00.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:06:04.000+00:00",
"children": null
},
{
"id": 6,
"menuName": "二级菜单22",
"level": 2,
"pid": 2,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:06:33.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:06:35.000+00:00",
"children": null
}
]
}
]
}
2. 添加权限
@Data
public class MenuRequest {
// 权限名称
private String menuName;
// 权限层级
private Integer level;
// 权限父id
private Integer pid;
}
controller:
/**
* 添加权限
*
* @param request
* @return
*/
@PostMapping("/permission/add")
public ResultEntity menuAdd(@RequestBody MenuRequest request) {
return menuService.menuAdd(request);
}
service:
@Override
public ResultEntity menuAdd(MenuRequest request) {
Menu menu = new Menu();
menu.setMenuName(request.getMenuName());
menu.setLevel(request.getLevel());
menu.setPid(request.getPid());
menu.setCreateId(0); // 根据具体业务逻辑获取当前登陆人id
menu.setCreateTime(new Date());
menu.setUpdateId(0);
menu.setUpdateTime(new Date());
menu.setState(0);
menuMapper.insertMenu(menu);
return new ResultEntity();
}
mapper:
<insert id="insertMenu">
insert menu(menu_name, level, pid, create_id, create_time, update_id, update_time, state)
values(
#{menuName,jdbcType=VARCHAR},#{level,jdbcType=INTEGER},#{pid,jdbcType=INTEGER},
#{createId,jdbcType=INTEGER},#{createTime,jdbcType=TIMESTAMP},#{updateId,jdbcType=INTEGER},
#{updateTime,jdbcType=TIMESTAMP},#{state,jdbcType=INTEGER}
)
</insert>
3. 删除权限
这里根据具体业务逻辑,需要注意删除某个权限时是否考虑删除该权限下的子权限。这里暂时不考虑,不影响使用。
controller:
/**
* 删除权限
*
* @param request
* @return
*/
@PostMapping("/permission/delete")
public ResultEntity menuDelete(@RequestBody MenuRequest request) {
return menuService.menuDelete(request);
}
service:
@Override
public ResultEntity menuDelete(MenuRequest request) {
ResultEntity result = new ResultEntity();
Integer id = request.getId();// 权限主键
if (Objects.isNull(id)) {
result.setCode(4001);
result.setMsg("参数为空");
return result;
}
menuMapper.updateState(id);
result.setCode(200);
result.setMsg("删除成功");
return result;
}
mapper:
<update id="updateState">
update menu
set state = 1
where id = #{id,jdbcType=INTEGER}
</update>
演示:这里删除 二级菜单11 id是 3
再次查询list结果:可以看到一级菜单1 下面的子菜单 二级菜单11 没有了,二级菜单11 下面的三级菜单也没有了(ps:这里三级菜单在数据库中的state状态还是可用的)
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"menuName": "一级菜单1",
"level": 1,
"pid": 0,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:03:56.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:04:01.000+00:00",
"children": [
{
"id": 4,
"menuName": "二级菜单12",
"level": 2,
"pid": 1,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:05:34.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:05:38.000+00:00",
"children": null
}
]
},
{
"id": 2,
"menuName": "一级菜单2",
"level": 1,
"pid": 0,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:04:26.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:04:30.000+00:00",
"children": [
{
"id": 5,
"menuName": "二级菜单21",
"level": 2,
"pid": 2,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:06:00.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:06:04.000+00:00",
"children": null
},
{
"id": 6,
"menuName": "二级菜单22",
"level": 2,
"pid": 2,
"state": 0,
"createId": 0,
"createTime": "2021-12-28T03:06:33.000+00:00",
"updateId": 0,
"updateTime": "2021-12-28T03:06:35.000+00:00",
"children": null
}
]
}
]
}
4. 角色列表
controller:
/**
* 角色列表
*
* @return
*/
@GetMapping("/role/list")
public ResultEntity roleList() {
return menuService.roleList();
}
service:
@Override
public ResultEntity roleList() {
ResultEntity result = new ResultEntity();
List<Role> roles = menuMapper.selectRoleAll();
result.setCode(200);
result.setMsg("查询成功");
result.setData(roles);
return result;
}
mapper:
<select id="selectRoleAll" resultType="com.zlp.demo.entity.Role">
select id,role_name,create_id,create_time,update_id,update_time,state
from user_role
where state = 0
</select>
测试结果:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"roleName": "超管",
"createId": 0,
"createTime": "2021-12-31T02:36:33.000+00:00",
"updateId": 0,
"updateTime": "2021-12-31T02:36:36.000+00:00",
"state": 0
},
{
"id": 2,
"roleName": "经理",
"createId": 0,
"createTime": "2021-12-31T02:36:53.000+00:00",
"updateId": 0,
"updateTime": "2021-12-31T02:36:59.000+00:00",
"state": 0
}
]
}
5. 添加角色
controller:
/**
* 创建角色
*
* @param request
* @return
*/
@PostMapping("/role/add")
public ResultEntity roleAdd(@RequestBody MenuRequest request) {
return menuService.roleAdd(request);
}
service:
@Override
public ResultEntity roleAdd(MenuRequest request) {
ResultEntity result = new ResultEntity();
Role role = new Role();
role.setRoleName(request.getRoleName());
role.setCreateId(0);
role.setCreateTime(new Date());
role.setUpdateId(0);
role.setUpdateTime(new Date());
role.setState(0);
menuMapper.createRole(role);
return result;
}
mapper:
<insert id="createRole">
insert into user_role(role_name,create_id,create_time,update_id,update_time,state)
values(#{roleName,jdbcType=VARCHAR},#{createId,jdbcType=INTEGER},
#{createTime,jdbcType=TIMESTAMP},
#{updateId,jdbcType=INTEGER},#{updateTime,jdbcType=TIMESTAMP},
#{state,jdbcType=INTEGER}
)
</insert>
6. 删除角色
controller:
/**
* 删除角色
*
* @param request
* @return
*/
@PostMapping("/role/delete")
public ResultEntity roleDelete(@RequestBody MenuRequest request) {
return menuService.roleDelete(request);
}
service:
@Override
public ResultEntity roleDelete(MenuRequest request) {
Integer id = request.getId();
menuMapper.updateRoleById(id);
return new ResultEntity();
}
mapper:
<update id="updateRoleById">
update user_role
set state = 1
where id = #{id,jdbcType=INTEGER}
</update>
7. 为角色添加权限
参数传递:
{
"menuIds": [1,3,8], // 权限id集合
"roleId": 3 // 角色id
}
// 权限ids
private List<Integer> menuIds;
// 角色id
private Integer roleId;
controller:
/**
* 为角色分配权限
*
* @param request
* @return
*/
@PostMapping("/add/role/permission")
public ResultEntity addRolePermission(@RequestBody MenuRequest request) {
return menuService.addRolePermission(request);
}
service:
@Override
public ResultEntity addRolePermission(MenuRequest request) {
List<Integer> menuIds = request.getMenuIds();
Integer roleId = request.getRoleId();
List<MenuRole> menuRoles = new ArrayList<>();
if (Objects.isNull(menuIds)) return new ResultEntity();
for (Integer menuId : menuIds) {
MenuRole menuRole = new MenuRole();
menuRole.setMenuId(menuId);
menuRole.setUserRoleId(roleId);
menuRole.setCreateId(0);
menuRole.setCreateTime(new Date());
menuRole.setUpdateId(0);
menuRole.setUpdateTime(new Date());
menuRoles.add(menuRole);
}
// 删除之前的角色权限
menuMapper.deleteMenuRoleByRoleId(menuIds);
// 添加新的角色权限
menuMapper.addMenuRole(menuRoles);
return new ResultEntity();
}
mapper:
<delete id="deleteMenuRoleByRoleId">
delete from menu_role_ref
where user_role_id in
<foreach collection="menuIds" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
<insert id="addMenuRole">
insert into menu_role_ref(menu_id,user_role_id,create_id,create_time,update_id,update_time)
values
<foreach collection="menuRoles" item="item" separator=",">
(#{item.menuId,jdbcType=INTEGER},#{item.userRoleId,jdbcType=INTEGER},
#{item.createId,jdbcType=INTEGER},#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateId,jdbcType=INTEGER},#{item.updateTime,jdbcType=TIMESTAMP}
)
</foreach>
</insert>
8. 用户添加角色
实际业务中user表中是有用户角色这个字段的,当然也可以单独创建一个中间表来存用户角色关系,这里就按user表中是存在角色字段的(ps:我们之前公司业务也是这样的)
参数:
{
"id": 1, // 用户id
"roleId": 3 // 角色id
}
controller:
/**
* 用户添加角色
*
* @param request
* @return
*/
@PostMapping("/add/user/role")
public ResultEntity addUserRole(@RequestBody MenuRequest request) {
return menuService.addUserRole(request);
}
service:
@Override
public ResultEntity addUserRole(MenuRequest request) {
ResultEntity resultEntity = new ResultEntity();
Integer roleId = request.getRoleId();
Integer userId = request.getId();
// 查询当前用户
User user = menuMapper.selectUserByid(userId);
if (Objects.isNull(user)) {
resultEntity.setCode(4000);
resultEntity.setMsg("未查到该用户");
return resultEntity;
}
// 更新当前用户角色
menuMapper.updateUserRole(roleId, userId);
resultEntity.setCode(200);
resultEntity.setMsg("成功");
return resultEntity;
}
mapper:
<update id="updateUserRole">
update user
set role_id = #{roleId,jdbcType=INTEGER}
where id = #{userId,jdbcType=INTEGER}
</update>
9. 查看用户拥有权限
重点,前面这些都是为实现这个接口的铺垫
参数:
{
"roleId": 3 // 当前用户的角色id
}
controller:
/**
* 查询用户权限
*
* @param request
* @return
*/
@PostMapping("/query/user/permission")
public ResultEntity queryPermission(@RequestBody MenuRequest request) {
return menuService.queryPermission(request);
}
service:
@Override
public ResultEntity queryPermission(MenuRequest request) {
ResultEntity resultEntity = new ResultEntity();
Integer roleId = request.getRoleId();
// 查询角色拥有的权限
List<MenuRole> menuRoles = menuMapper.selectMenuRoleByRoleId(roleId);
if (Objects.isNull(menuRoles)) {
resultEntity.setCode(4000);
resultEntity.setMsg("该角色暂没添加权限");
return resultEntity;
}
List<Integer> menuIds = menuRoles.stream().map(MenuRole::getMenuId).collect(Collectors.toList());
// 查询权限信息
List<Menu> menus = menuMapper.selectMenuByIds(menuIds);
Map<Integer, List<Menu>> pidMap = menus.stream().collect(Collectors.groupingBy(Menu::getPid));
Menu menu = new Menu();
// 递归调用填充数据
addMenuByPid(menu, pidMap);
resultEntity.setMsg("success");
resultEntity.setCode(200);
resultEntity.setData(menu.getChildren());
return resultEntity;
}
/**
* 将菜单分级嵌套组合
*
*/
private void addMenuByPid(Menu menu, Map<Integer, List<Menu>> pidMap) {
if (Objects.isNull(menu.getId())) { // 一级菜单
menu.setChildren(pidMap.get(0));
} else {
// 获取当前菜单的子菜单
List<Menu> childMenu = pidMap.get(menu.getId());
// 将子菜单插入当前菜单
menu.setChildren(childMenu);
}
if (!CollectionUtils.isEmpty(menu.getChildren())) { // 子菜单为空时跳出递归
// 当前菜单下填充子菜单
menu.getChildren().forEach(m -> addMenuByPid(m, pidMap));
}
}
mapper:
<select id="selectMenuRoleByRoleId" resultType="com.zlp.demo.entity.MenuRole">
select menu_id,user_role_id,create_id,create_time,update_id,update_time
from menu_role_ref
where user_role_id = #{roleId,jdbcType=INTEGER}
</select>
<select id="selectMenuByIds" resultType="com.zlp.demo.entity.Menu">
select id, menu_name, level, pid, create_id, create_time, update_id, update_time, state
from menu
where id in
<foreach collection="menuIds" item="item" separator="," open="(" close=")">
#{item}
</foreach>
and state = 0
</select>