目录
本文想要通过这个小实例,实现根据用户不同角色的登录分配给用户不同的功能,通过分配给用户角色的动态路由来实现这个功能。
一、接口文档
登录接口
1、生成验证码
URL
json
GET /captcha
参数
json
无
返回
json
{
"msg": "操作成功",
"code": 200,
"data": {
"uuid": "b71fafb1a91b4961afb27372bd3af77c",
"captcha": "data:image/png;base64,iVBORw0KGgoAAAA",
"code": "nrew"
}
}
2、登录
URL
json
POST /login
参数
json
{
"captcha": "nrew",
"password": "123456",
"username": "admin",
"uuid": "b71fafb1a91b4961afb27372bd3af77c"
}
返回
json
{
"msg": "登录成功",
"code": 200,
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNjg5NjYxODUwLCJleHAiOjE2OTA5NTc4NTB9.Bq0pExJcSgaQD94qAYVSx4W__K8FWGeY7iUJKMyTIYFofZoqa2VfEvl8Na96kt2wYbjFImHmQntkdce6cHQ8_A",
"expire": 1296000
}
}
3、Token校验
URL
json
GET /checkToken
参数
json
HttpServletRequest request
返回
json
{
"msg": "操作成功",
"code": 200,
"status": "ok"
}
{
"msg": "操作成功",
"code": 200,
"status": "error"
}
动态路由
URL
1
GET /sys/user/getRouters
参数
无
返回
json
{
"msg": "操作成功",
"code": 200,
"data": {
"userId": 1,
"username": "admin",
"password": "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
"realName": "管理员",
"contact": "",
"mobile": "15679711120",
"status": 1
},
"roles": "超级管理员",
"routers": [
{
"name": "系统管理",
"path": "/sys",
"hidden": "false",
"redirect": "noRedirect",
"component": "Layout",
"alwaysShow": true,
"meta": {
"title": "系统管理",
"icon": "system"
},
"children": [
{
"name": "管理员管理",
"path": "/user",
"hidden": "false",
"component": "sys/user/index",
"meta": {
"title": "管理员管理",
"icon": "user"
}
}
]
}
]
}
二、数据库表结构
下面RBAC的权限认证例子需要用到下面的五张表,五张表之间相互配置实现该效果。

五张表结构和数据如下:
role表
sql
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`role_id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '角色名称',
`type` int(11) DEFAULT NULL,
`remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
`create_time` timestamp(0) DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`role_id`) USING BTREE,
UNIQUE INDEX `role_name`(`role_name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员', 1, '具有所有权限的操作', '2017-11-21 11:42:11');
INSERT INTO `role` VALUES (7, '普通管理员', 2, '普通角色', '2020-12-01 19:15:29');
SET FOREIGN_KEY_CHECKS = 1;
menu表
sql
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`menu_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '菜单名称',
`path` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '菜单URL',
`component` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '组件路径',
`menu_type` tinyint(1) DEFAULT NULL,
`status` int(11) DEFAULT NULL,
`icon` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '菜单图标',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`hidden` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '菜单管理' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, 0, '系统管理', '/sys', 'Layout', 1, 1, 'system', 3, 'false');
INSERT INTO `menu` VALUES (2, 1, '用户管理', '/user', 'sys/user/index', 2, 1, 'user', 10, 'false');
INSERT INTO `menu` VALUES (3, 1, '角色管理', '/role', 'sys/role/index', 2, 1, 'people', 11, 'false');
INSERT INTO `menu` VALUES (4, 1, '菜单管理', '/menu', 'sys/menu/index', 2, 1, 'tree-table', 12, 'false');
INSERT INTO `menu` VALUES (5, 1, '日志管理', '/log', 'sys/log/index', 2, 1, 'form', 13, 'false');
INSERT INTO `menu` VALUES (6, 9, '居民管理', '/person', 'sys/person/index', 1, 1, 'peoples', 5, 'false');
INSERT INTO `menu` VALUES (7, 9, '小区管理', '/community', 'sys/community/index', 1, 1, 'table', 4, 'false');
INSERT INTO `menu` VALUES (8, 14, '访客登记', '/manual', 'sys/manual/index', 2, 1, 'logininfor', 9, 'false');
INSERT INTO `menu` VALUES (9, 0, '物业管理', '/community', 'Layout', 1, 1, 'tab', 1, 'false');
INSERT INTO `menu` VALUES (10, 1, '系统设置', '/server', 'sys/liveServer/index', 2, 1, 'user', 15, 'true');
INSERT INTO `menu` VALUES (11, 9, '摄像头管理', '/camera', 'sys/camera/index', 1, 1, 'radio', 14, 'true');
INSERT INTO `menu` VALUES (12, 14, '人脸识别', '/identify', 'sys/inOut/index', 2, 1, 'eye-open', 7, 'false');
INSERT INTO `menu` VALUES (13, 14, '出入记录', '/inOut', 'sys/inOut/inOutRecord', 2, 1, 'log', 8, 'false');
INSERT INTO `menu` VALUES (14, 0, '门禁管理', '/manualMgr', 'Layout', 1, 1, 'monitor', 2, 'false');
INSERT INTO `menu` VALUES (15, 9, '小区地图', '/map', 'sys/map/index', 2, 1, 'international', 6, 'false');
SET FOREIGN_KEY_CHECKS = 1;
role_menu表:
sql
DROP TABLE IF EXISTS `role_menu`;
CREATE TABLE `role_menu` (
`role_id` int(11) NOT NULL COMMENT '角色ID',
`menu_id` int(11) NOT NULL COMMENT '菜单ID',
PRIMARY KEY (`role_id`, `menu_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '角色与菜单对应关系' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role_menu
-- ----------------------------
INSERT INTO `role_menu` VALUES (1, 1);
INSERT INTO `role_menu` VALUES (1, 2);
INSERT INTO `role_menu` VALUES (1, 3);
INSERT INTO `role_menu` VALUES (1, 4);
INSERT INTO `role_menu` VALUES (1, 5);
INSERT INTO `role_menu` VALUES (1, 6);
INSERT INTO `role_menu` VALUES (1, 7);
INSERT INTO `role_menu` VALUES (1, 8);
INSERT INTO `role_menu` VALUES (1, 9);
INSERT INTO `role_menu` VALUES (1, 10);
INSERT INTO `role_menu` VALUES (1, 11);
INSERT INTO `role_menu` VALUES (1, 12);
INSERT INTO `role_menu` VALUES (1, 13);
INSERT INTO `role_menu` VALUES (1, 14);
INSERT INTO `role_menu` VALUES (1, 15);
INSERT INTO `role_menu` VALUES (7, 1);
INSERT INTO `role_menu` VALUES (7, 2);
INSERT INTO `role_menu` VALUES (7, 6);
INSERT INTO `role_menu` VALUES (7, 8);
INSERT INTO `role_menu` VALUES (7, 9);
INSERT INTO `role_menu` VALUES (7, 14);
SET FOREIGN_KEY_CHECKS = 1;
user表
sql
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
`password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '密码',
`real_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '真实姓名',
`contact` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '联系人',
`mobile` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '手机号',
`status` int(11) DEFAULT NULL COMMENT '状态 0正常 1禁用',
PRIMARY KEY (`user_id`) USING BTREE,
UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '系统用户' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', '管理员', '张三', '15679711120', 1);
INSERT INTO `user` VALUES (39, 'test', '96cae35ce8a9b0244178bf28e4966c2ce1b8385723a96a6b838858cdd6ca0a1e', '张三', '', '', 1);
SET FOREIGN_KEY_CHECKS = 1;
user_role表
sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` int(11) NOT NULL COMMENT '用户ID',
`role_id` int(11) NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户与角色对应关系' ROW_FORMAT = Compact;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1);
INSERT INTO `user_role` VALUES (39, 7);
SET FOREIGN_KEY_CHECKS = 1;
表之间关系

三、接口开发讲解
1、验证码生成
java
@GetMapping("/captcha")
public Result getCaptcha(){
SpecCaptcha specCaptcha = new SpecCaptcha(130,48,4);
String code = specCaptcha.text().toUpperCase();
String uuid = IdUtil.simpleUUID();
redisTemplate.opsForValue().set(uuid,code,120, TimeUnit.SECONDS);
HashMap<String,String> res = new HashMap<>();
res.put("uuid",uuid);
res.put("captcha",specCaptcha.toBase64());
res.put("code",code);
return Result.ok().put("data",res);
}
SpecCaptcha specCaptcha = new SpecCaptcha(130,48,4);随机生成长130宽48,位数为4的验证码。
String code = specCaptcha.text().toUpperCase();将验证码全部转为大写。
String uuid = IdUtil.simpleUUID();生成唯一标识。
redisTemplate.opsForValue().set(uuid,code,120, TimeUnit.SECONDS);使用redisTemplate将验证方法redis中。
组装结果返回给前端。
2、验证码生成
java
@PostMapping("/login")
public Result login(@RequestBody LoginDTO loginDTO,HttpSession httpSession){
// 1、获取验证码
String code = (String) redisTemplate.opsForValue().get(loginDTO.getUuid());
// 2、判断验证码是否失效(时效性)
if(code == null){
return Result.error("验证码已失效");
}
// 3、验证验证码是否正确(正确性)
if(!code.equals(loginDTO.getCaptcha())){
return Result.error("验证码错误");
}
// 4、判断用户名和密码是否正确
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",loginDTO.getUsername());
User user = userService.getOne(queryWrapper);
// 如果用户名为空,则说明没有该用户
if(user == null){
return Result.error("用户名错误");
}
if(!user.getPassword().equals(SecureUtil.sha256(loginDTO.getPassword()))){
return Result.error("密码错误");
}
// 判断用户的状态
if(user.getStatus() !=1){
return Result.error("用户状态有问题");
}
// 存储用户信息
httpSession.setAttribute("user",user);
// todo 用redis存储token
// 生成token
String token = jwtUtil.createToken(user.getUserId().toString());
HashMap<String,Object> hp = new HashMap<>();
hp.put("token",token);
hp.put("expire",jwtUtil.getExpire());
return Result.ok().put("data",hp);
}
1、判断有没有验证码(验证码是不是已经过期了)
2、判断验证码是否正确
3、判断是否有该用户
4、判断用户密码是否正确
5、存储信息并且生成token
6、组装结果返回给前端
3、验证Token
java
@GetMapping("/checkToken")
public Result checkToken(HttpServletRequest request){
// 从请求头中取出token
// todo 为什么在请求头中
String token = request.getHeader("token");
System.out.println("token:"+token);
// 验证token
boolean res = jwtUtil.checkToken(token);
System.out.println("res:"+res);
if(!res){
return Result.ok().put("status","error");
}
return Result.ok().put("status","ok");
}
1、从请求头中得到Token
2、验证token,升级功能将token存到redis中,判断redis中有没有该token,判断是不是失效
3、验证token正确性
4、返回结果
4、动态路由
按照上面的接口返回结果首先封装实体类,让其按照想要的结果进行返回。
1、实现代码
java
@GetMapping("/getRouters")
public Result getRouters(HttpSession session){
// 从session中获取用户的信息
User user = (User) session.getAttribute("user");
// 获取用户的角色信息
HashMap<String,Object> hp = userService.getUserRole(user.getUserId());
for(String key :hp.keySet()){
System.out.println(key);
System.out.println(hp.get(key));
}
//获取roleId和roleName
Integer roleId = (Integer) hp.get("roleId");
String role = hp.get("role").toString();
// 获取用户的动态路由
// 获取根路由
List<RouterVO> routerList = userService.getRouters(roleId);
// 根据跟路由的id获取子路由
for(RouterVO router:routerList){
List<ChildRouterVO> childRouterList = userService.getChildrenRouters(router.getMenuId());
router.setChildren(childRouterList);
router.setAlwaysShow(true);
router.setRedirect("noRedirect");
}
return Result.ok().put("data",user).put("roles",role).put("routers",routerList);
}
2、RouterVO实体类
java
package com.example.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.omg.CORBA.PRIVATE_MEMBER;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RouterVO {
private Integer menuId;
private String name;
private String path;
private String hidden;
private String redirect;
private String component;
private Boolean alwaysShow;
private MetaVO meta;
private List<ChildRouterVO> children;
}
利用List列表封装children,并且用MetaVO封装meta
3、MetaVO
java
package com.example.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MetaVO {
private String title;
private String icon;
}
4、ChildRouterVO
java
package com.example.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChildRouterVO {
private Integer menuId;
private String name;
private String path;
private String hidden;
private String component;
private MetaVO meta;
}
5、实现思路
User user = (User) session.getAttribute("user");获取结果的第一部分

java
HashMap<String,Object> hp = userService.getUserRole(user.getUserId());
//获取roleId和roleName
Integer roleId = (Integer) hp.get("roleId");
String role = hp.get("role").toString();
根据user_id获取当前用户的角色 和角色id
实现sql:
xml
<select id="getUserRole" resultType="java.util.HashMap">
select role_name as role,role.role_id as roleId from user join user_role on user.user_id = user_role.user_id join role on
user_role.role_id = role.role_id where user.user_id = #{userId}
</select>
List<RouterVO> routerList = userService.getRouters(roleId);根据roleId获取用户所有的根路由。
实现sql:
xml
<select id="getRouters" resultMap="getRouters">
select menu.* from role_menu join menu on role_menu.menu_id = menu.menu_id where role_id = #{roleId} and parent_id = 0 ORDER BY sort asc
</select>
<resultMap id="getRouters" type="com.example.vo.RouterVO">
<id property="menuId" column="menu_id"/>
<result property="name" column="name"/>
<result property="path" column="path"/>
<result property="hidden" column="hidden"/>
<result property="component" column="component"/>
<association property="meta" javaType="com.example.vo.MetaVO">
<result property="title" column="name"/>
<result property="icon" column="icon"/>
</association>
</resultMap>
获取根路由下所有的子路由,然后将所有的子路由封装回去根路由:
java
for(RouterVO router:routerList){
List<ChildRouterVO> childRouterList = userService.getChildrenRouters(router.getMenuId());
router.setChildren(childRouterList);
}
xml
<select id="getChildrenRouters" resultMap="getChildRenRouters">
select menu.* from role_menu join menu on role_menu.menu_id = menu.menu_id where parent_id = #{menuId} ORDER BY sort asc
</select>
<resultMap id="getChildRenRouters" type="com.example.vo.ChildRouterVO">
<id property="menuId" column="menu_id"/>
<result property="name" column="name"/>
<result property="path" column="path"/>
<result property="hidden" column="hidden"/>
<result property="component" column="component"/>
<association property="meta" javaType="com.example.vo.MetaVO">
<result property="title" column="name"/>
<result property="icon" column="icon"/>
</association>
</resultMap>