RBAC(权限认证)小例子

目录

本文想要通过这个小实例,实现根据用户不同角色的登录分配给用户不同的功能,通过分配给用户角色的动态路由来实现这个功能。

一、接口文档

登录接口

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;
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>
相关推荐
神仙别闹2 小时前
基于QT(C++)实现学本科教务系统(URP系统)
数据库·c++·qt
青蛙大侠公主2 小时前
Thread及其相关类
java·开发语言
Coder_Boy_2 小时前
DDD从0到企业级:迭代式学习 (共17章)之 四
java·人工智能·驱动开发·学习
2301_768350232 小时前
MySQL为什么选择InnoDB作为存储引擎
java·数据库·mysql
上海蓝色星球2 小时前
迈向智慧电网新纪元:上海蓝色星球数字孪生变电主子站系统
运维·数据库
是大芒果2 小时前
数据库表设计
数据库
派大鑫wink2 小时前
【Java 学习日记】开篇:以日记为舟,渡 Java 进阶之海
java·笔记·程序人生·学习方法
哥哥还在IT中2 小时前
MySQL order by 如何优化
数据库·mysql
积跬步,慕至千里3 小时前
postgre数据库大批量快速导出方法总结
数据库·postgres