本章实现的功能是,某个用户登录时,如何查找该用户的菜单权限
Spring security认证过程
1、用户使用用户名和密码进行登录。
2、Spring Security将获取到的用户名和密码封装成一个实现了Authentication接口的UsernamePasswordAuthenticationToken。
3、将上述产生的token对象传递给AuthenticationManager进行登录认证。
4、AuthenticationManager认证成功后将会返回一个封装了用户权限等信息的Authentication对象。
5、通过调用SecurityContextHolder.getContext().setAuthentication(...)将AuthenticationManager返回的Authentication对象赋予给当前的SecurityContext。
6、认证成功后,用户就可以继续操作去访问其它受保护的资源了,通过调用SecurityContextHolder.getContext().getAuthentication()获取保存在SecurityContext中的Authentication对象进行相关的权限鉴定。
系统权限设计
设计基础:任何权限的需求,都是为广义的用户分配角色,角色拥有广义的权限。用户、角色、菜单(权限)三大核心表,加上用户角色、角色菜单两个映射表。就可以通过登录的用户来获取权限列表,再间接获取用户菜单列表。
系统权限表结构设计如下
用户表
CREATE TABLE `crm_sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '系统用户名',
`password` varchar(60) NOT NULL COMMENT '密码',
`nickname` varchar(255) DEFAULT NULL COMMENT '昵称',
`headImgUrl` varchar(255) DEFAULT NULL COMMENT '头像',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`telephone` varchar(30) DEFAULT NULL COMMENT '电话',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`birthday` varchar(10) DEFAULT NULL COMMENT '生日',
`sex` tinyint(1) DEFAULT NULL COMMENT '性别',
`isEnable` int(1) DEFAULT NULL COMMENT '状态 0.已停用 1.正常',
`isDel` int(1) DEFAULT NULL COMMENT '是否删除(0.已删除 1.正常)',
`createTime` varchar(22) DEFAULT NULL,
`updateTime` varchar(22) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';
角色表
CREATE TABLE `crm_sys_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '角色名称',
`description` varchar(100) DEFAULT NULL COMMENT '描述',
`isEnable` tinyint(1) DEFAULT NULL COMMENT '状态 0.已停用 1.正常',
`isDel` tinyint(1) DEFAULT NULL COMMENT '是否删除(0.已删除 1.正常)',
`createTime` varchar(22) DEFAULT NULL,
`updateTime` varchar(22) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='系统角色表';
菜单表
CREATE TABLE `crm_sys_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parentId` int(11) NOT NULL COMMENT '上级菜单',
`name` varchar(50) NOT NULL COMMENT '菜单名称',
`css` varchar(30) DEFAULT NULL COMMENT '图标样式',
`href` varchar(1000) DEFAULT NULL COMMENT '链接',
`type` tinyint(1) NOT NULL COMMENT '菜单类型 1:菜单 2:按钮',
`permission` varchar(50) DEFAULT NULL COMMENT '按钮权限',
`sequence` int(11) NOT NULL COMMENT '排序',
`isEnable` int(1) DEFAULT NULL COMMENT '状态 0.已停用 1.正常',
`isDel` int(1) DEFAULT NULL COMMENT '是否删除(0.已删除 1.正常)',
`createTime` varchar(22) DEFAULT NULL,
`updateTime` varchar(22) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COMMENT='系统菜单表';
用户与角色关联表
CREATE TABLE `crm_sys_user_role` (
`userId` int(11) NOT NULL,
`roleId` int(11) NOT NULL,
PRIMARY KEY (`userId`,`roleId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户与角色关联表';
角色与菜单关联表
CREATE TABLE `crm_sys_role_menu` (
`roleId` int(11) NOT NULL,
`menuId` int(11) NOT NULL,
PRIMARY KEY (`roleId`,`menuId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统角色与菜单关联表';
查找用户的菜单权限树结构
业务代理
@GetMapping("/current")
public List<SysMenuDto> menuCurrent() {
LoginUser loginUser = UserUtil.getLoginUser();
Long id = Long.valueOf(String.valueOf(loginUser.getId()));
List<SysMenuDto> list = iSysMenuService.listByUserId(id);
final List<SysMenuDto> menus = list.stream().filter(l -> l.getType().equals(1))
.collect(Collectors.toList());
//支持多级菜单
List<SysMenuDto> firstLevel = menus.stream().filter(p -> p.getParentId().equals(0)).collect(Collectors.toList());
firstLevel.parallelStream().forEach(m -> {
setChild(m, menus);
});
return firstLevel;
}
/**
* 设置子元素
* @param m
* @param menus
*/
private void setChild(SysMenuDto m, List<SysMenuDto> menus) {
List<SysMenuDto> child = menus.parallelStream().filter(a -> a.getParentId().equals(m.getId())).collect(Collectors.toList());
m.setChild(child);
if (!CollectionUtils.isEmpty(child)) {
child.parallelStream().forEach(c -> {
//递归设置子元素,多级菜单支持
setChild(c, menus);
});
}
}
Authentication公用类
public class UserUtil {
public static LoginUser getLoginUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
if (authentication instanceof AnonymousAuthenticationToken) {
return null;
}
if (authentication instanceof UsernamePasswordAuthenticationToken) {
return (LoginUser) authentication.getPrincipal();
}
}
return null;
}
}
sql代码
@Select("select distinct m.* " +
"from crm_sys_menu m " +
"inner join crm_sys_role_menu rm on m.id = rm.menuId " +
"inner join crm_sys_user_role ru on ru.roleId = rm.roleId " +
"where isDel = 1 and isEnable = 1 and ru.userId = #{userId} order by m.sequence")
List<CrmSysMenuPo> listByUserId(Long userId);
返回结果
[
{
"id": 1,
"parentId": 0,
"name": "系统管理",
"css": "fa-gears",
"href": "",
"type": 1,
"permission": "",
"sequence": 1,
"isEnable": true,
"isDel": true,
"createTime": "2017-10-05 21:59:18",
"child": [
{
"id": 2,
"parentId": 1,
"name": "用户",
"css": "fa-users",
"href": "sys/user/queryList",
"type": 1,
"permission": "",
"sequence": 101,
"isEnable": true,
"isDel": true,
"createTime": "2017-10-05 21:59:18",
"child": [
],
"serialVersion": -123142
},
{
"id": 3,
"parentId": 1,
"name": "菜单",
"css": "fa-cog",
"href": "sys/menu/queryList",
"type": 1,
"permission": "",
"sequence": 102,
"isEnable": true,
"isDel": true,
"createTime": "2017-10-05 21:59:18",
"child": [
],
"serialVersion": -123142
},
{
"id": 4,
"parentId": 1,
"name": "角色",
"css": "fa-user-secret",
"href": "sys/role/queryList",
"type": 1,
"permission": "",
"sequence": 103,
"isEnable": true,
"isDel": true,
"createTime": "2017-10-05 21:59:18",
"child": [
],
"serialVersion": -123142
}
],
"serialVersion": -123142
}
]