Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【三、系统权限】

x33g5p2x  于2022-07-10 转载在 Java  
字(32.6k)|赞(0)|评价(0)|浏览(487)
Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【一、前端】跳转
Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【二、后端】跳转
Java 之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【三、系统权限】跳转

Java 之SpringBoot+Vue实现后台管理系统的开发项目源代码

https://download.csdn.net/download/qq_44757034/85959712

一、权限缓存

因为上面我在获取用户权限那里添加了个缓存,这时候问题来了,

就是权限缓存的实时更新问题,
比如当后台更新某个管理员的权限角色信息的时候如果权限缓存信息没有实时更新,

就会出现操作无效的问题,那么我们现在点定义几个方法,

用于清除某个用户或角色或者某个菜单的权限的方法:

@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    @Autowired
    private SysRoleService sysUserService;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    @Lazy
    private SysMenuService sysMenuService;
    @Autowired
    private RedisUtil redisUtil;
    @Override
    public SysUser getByUserName(String username) {
        return getOne(new QueryWrapper<SysUser>().eq("username",username));
    }
    @Override
    public String getUserAuthorityInfo(Long userId) {
        SysUser sysUser = sysUserMapper.selectById(userId);
        //通过用户id获取对应的角色信息
        String authority = null;
        if(redisUtil.hasKey("GrantedAuthority:"+sysUser.getUsername())){
            authority = (String) redisUtil.get("GrantedAuthority:" + sysUser.getUsername());
        }else {
            //获取角色编码
            //通过用户id,查出对应的用户角色id,通过角色id查询,对应用户的角色信息
            List<SysRole> roles = sysUserService.list(new QueryWrapper<SysRole>().inSql("id", "select role_id from sys_user_role where user_id = " + userId));
            if(roles.size() > 0){
                String roleCode = roles.stream().map(r -> "ROLE_"+r.getCode()).collect(Collectors.joining(","));
                authority = roleCode.concat(",");
            }
            //获取菜单操作权限
            List<Long> menuIds = sysUserMapper.getNavMenuIds(userId);
            if(menuIds.size() > 0){
                List<SysMenu> sysMenus = sysMenuService.listByIds(menuIds);
                String menuPerms = sysMenus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));
                authority = authority.concat(menuPerms);
            }
            redisUtil.set("GrantedAuthority:"+sysUser.getUsername(),authority,60 * 60);
        }
        return authority;
    }
}

添加断点

登录以后发送请求

第一次访问:Redis当中的权限信息为空

第二次访问:Redis对应的权限信息不为空

完善权限其他方法

void clearUserAuthorityInfo(String username);

    void clearUserAuthorityInfoByRoleId(Long roleId);

    void clearUserAuthorityInfoByMenuId(Long menuId);

完善,设置清楚用户的权限信息,在更新对应的角色和用户信息的时候
SysUserServiceImpl

@Override
    public String getUserAuthorityInfo(Long userId) {
        SysUser sysUser = sysUserMapper.selectById(userId);
        //通过用户id获取对应的角色信息
        String authority = null;
        if(redisUtil.hasKey("GrantedAuthority:"+sysUser.getUsername())){
            authority = (String) redisUtil.get("GrantedAuthority:" + sysUser.getUsername());
        }else {
            //获取角色编码
            //通过用户id,查出对应的用户角色id,通过角色id查询,对应用户的角色信息
            List<SysRole> roles = sysUserService.list(new QueryWrapper<SysRole>().inSql("id", "select role_id from sys_user_role where user_id = " + userId));
            if(roles.size() > 0){
                String roleCode = roles.stream().map(r -> "ROLE_"+r.getCode()).collect(Collectors.joining(","));
                authority = roleCode.concat(",");
            }
            //获取菜单操作权限
            List<Long> menuIds = sysUserMapper.getNavMenuIds(userId);
            if(menuIds.size() > 0){
                List<SysMenu> sysMenus = sysMenuService.listByIds(menuIds);
                String menuPerms = sysMenus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));
                authority = authority.concat(menuPerms);
            }
            redisUtil.set("GrantedAuthority:"+sysUser.getUsername(),authority,60 * 60);
        }
        return authority;
    }

    @Override
    public void clearUserAuthorityInfo(String username) {
        redisUtil.del("GrantedAuthority:"+username);
    }

    @Override
    public void clearUserAuthorityInfoByRoleId(Long roleId) {
        List<SysUser> sysUsers = this.list(new QueryWrapper<SysUser>()
                .inSql("id", "select user_id from sys_user_role where role_id = " + roleId));
        sysUsers.forEach(u ->{
            this.clearUserAuthorityInfo(u.getUsername());
        });
    }

    @Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
       List<SysUser> sysUsers = sysUserMapper.listByMenuId(menuId);
        sysUsers.forEach(u ->{
            this.clearUserAuthorityInfo(u.getUsername());
        });
    }

List<SysUser> listByMenuId(Long menuId);

<select id="listByMenuId" resultType="cn.itbluebox.springbootadminvue.entity.SysUser">
        SELECT DISTINCT
            su.*
        FROM
            sys_user_role ur
                LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id
                LEFT JOIN sys_user su on ur.user_id = su.id
        WHERE
            rm.menu_id = #{menuId}
    </select>

完善
SysUserServiceImpl

@Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
       List<SysUser> sysUsers = sysUserMapper.listByMenuId(menuId);
        sysUsers.forEach(u ->{
            this.clearUserAuthorityInfo(u.getUsername());
        });
    }

二、退出数据返回

1、创建JwtLogoutSuccessHandler

@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {

    @Autowired
    JwtUtils jwtUtils;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        if(authentication != null){
            new SecurityContextLogoutHandler().logout(request,response,authentication);
        }
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = response.getOutputStream();

        response.setHeader(jwtUtils.getHeader(), null);

        Result result = Result.success("成功");

        outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));

        outputStream.flush();
        outputStream.close();
    }
}

2、完善SecurityConfig

@Autowired
    private JwtLogoutSuccessHandler jwtLogoutSuccessHandler;
.and()
        .logout()
        .logoutSuccessHandler(jwtLogoutSuccessHandler)

三、前后端对接(菜单)

1、前后端对接的问题

因为我们之前开发前端的时候,我们都是使用mockjs返回随机数据的,
一般来说问题不会很大,我就怕有些同学再去掉mock之后,和后端对接却显示不出数据,这就尴尬了。
这时候我建议你去看我的开发视频哈。

后面因为都是接口的增删改查,难度其实不是特别大,所以大部分时候我都会直接贴代码,

2、菜单接口开发

我们先来开发菜单的接口,

因为这3个表:用户表、角色表、菜单表,才有菜单表是不需要通过其他表来获取信息的。

比如用户需要关联角色,角色需要关联菜单,而菜单不需要主动关联其他表。

因此菜单表的增删改查是最简单的。

再回到我们的前端项目,登录完成之后我们通过WT获取项目的导航菜单和权限,

那么接下来我们就先编写这个接口。

获取菜单导航和权限的链接是/sys/menu/nav,然后我们的菜单导航的json数据应该是这样的:

删除下面两个接口,中间表的Controller是自动生成的

BaseController

@Autowired
    SysUserService sysUserService;

    @Autowired
    SysRoleService sysRoleService;

    @Autowired
    SysMenuService sysMenuService;

    @Autowired
    SysUserRoleService sysUserRoleService;

    @Autowired
    SysRoleMenuService sysRoleMenuService;

@Data
public class SysMenuDto implements Serializable {

    private Long id;
    private String name;
    private String title;
    private String icon;
    private String path;
    private String component;
    private List<SysMenuDto> children = new ArrayList<>();

}

@RestController
@RequestMapping("/sys/menu")
public class SysMenuController extends BaseController {

    @GetMapping("/nav")
    public Result nav(Principal principal){

        SysUser sysUser = sysUserService.getByUserName(principal.getName());

        //获取权限信息
        String authorityInfo = sysUserService.getUserAuthorityInfo(sysUser.getId());

        String[] authorityInfoArray = StringUtils.tokenizeToStringArray(authorityInfo, "");

        //获取导航栏信息
        List<SysMenuDto> navs = sysMenuService.getCurrentUserNav();

        return Result.success(
                MapUtil.builder()
                        .put("authoritys",authorityInfoArray)
                        .put("nav",navs)
                        .map()
        );
    }
}

public interface SysMenuService extends IService<SysMenu> {
    List<SysMenuDto> getCurrentUserNav();
}

完善SysMenu

/**
     * 排序
     */
    @TableField("orderNum")
    private Integer orderNum;

    @TableField(exist = false)
    private List<SysMenu> children = new ArrayList<>();

完善SysMenuServiceImpl

private List<SysMenu> buildTreeMenu(List<SysMenu> menus) {
        List<SysMenu> finalMenus = new ArrayList<>();
        //先各自寻找到自己的孩子
        for (SysMenu menu:menus){
            for(SysMenu e:menus){
                if(menu.getId().equals(e.getParentId())){
                    menu.getChildren().add(e);
                }
            }
            if(menu.getParentId() == 0){
                finalMenus.add(menu);
            }
        }
        log.info(JSONUtil.toJsonStr(finalMenus));
        //提取出父亲
        return finalMenus;
    }
    private List<SysMenuDto> convert(List<SysMenu> menuTree){
        List<SysMenuDto> menuDtos = new ArrayList<>();
        menuTree.forEach(m ->{
            SysMenuDto dto = new SysMenuDto();
            dto.setId(m.getId());
            dto.setName(m.getPerms());
            dto.setTitle(m.getName());
            dto.setComponent(m.getComponent());
            dto.setPath(m.getPath());
            if(m.getChildren().size() > 0){
                //子节点调用当前方法进行再次替换
                dto.setChildren(convert(m.getChildren()));
            }
            menuDtos.add(dto);
        });
        return menuDtos;
    }

3、运行测试

http://localhost:8081/sys/menu/nav
Authorization

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTY1NjU1ODY4NywiZXhwIjoxNjU3MTYzNDg3fQ.Nso0BloTp0eggRU2eyMJdkY27ZBow8OfoZWcHSk2USWNv9jtu1L_e9bvnBNmm_82hzr8JcpKFNE3U6wvI0m2xw

数据不太全面,在这里我们添加一些数据,执行一些SQL语句

/*
Navicat MySQL Data Transfer

Source Server         : pro-markerhub
Source Server Version : 50727
Source Host           : 129.204.23.53:3306
Source Database       : vueadmin

Target Server Type    : MYSQL
Target Server Version : 50727
File Encoding         : 65001

Date: 2021-01-30 21:02:31
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',
  `name` varchar(64) NOT NULL,
  `path` varchar(255) DEFAULT NULL COMMENT '菜单URL',
  `perms` varchar(255) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
  `component` varchar(255) DEFAULT NULL,
  `type` int(5) NOT NULL COMMENT '类型     0:目录   1:菜单   2:按钮',
  `icon` varchar(32) DEFAULT NULL COMMENT '菜单图标',
  `orderNum` int(11) DEFAULT NULL COMMENT '排序',
  `created` datetime NOT NULL,
  `updated` datetime DEFAULT NULL,
  `statu` int(5) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES ('1', '0', '系统管理', '', 'sys:manage', '', '0', 'el-icon-s-operation', '1', '2021-01-15 18:58:18', '2021-01-15 18:58:20', '1');
INSERT INTO `sys_menu` VALUES ('2', '1', '用户管理', '/sys/users', 'sys:user:list', 'sys/User', '1', 'el-icon-s-custom', '1', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');
INSERT INTO `sys_menu` VALUES ('3', '1', '角色管理', '/sys/roles', 'sys:role:list', 'sys/Role', '1', 'el-icon-rank', '2', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');
INSERT INTO `sys_menu` VALUES ('4', '1', '菜单管理', '/sys/menus', 'sys:menu:list', 'sys/Menu', '1', 'el-icon-menu', '3', '2021-01-15 19:03:45', '2021-01-15 19:03:48', '1');
INSERT INTO `sys_menu` VALUES ('5', '0', '系统工具', '', 'sys:tools', null, '0', 'el-icon-s-tools', '2', '2021-01-15 19:06:11', null, '1');
INSERT INTO `sys_menu` VALUES ('6', '5', '数字字典', '/sys/dicts', 'sys:dict:list', 'sys/Dict', '1', 'el-icon-s-order', '1', '2021-01-15 19:07:18', '2021-01-18 16:32:13', '1');
INSERT INTO `sys_menu` VALUES ('7', '3', '添加角色', '', 'sys:role:save', '', '2', '', '1', '2021-01-15 23:02:25', '2021-01-17 21:53:14', '0');
INSERT INTO `sys_menu` VALUES ('9', '2', '添加用户', null, 'sys:user:save', null, '2', null, '1', '2021-01-17 21:48:32', null, '1');
INSERT INTO `sys_menu` VALUES ('10', '2', '修改用户', null, 'sys:user:update', null, '2', null, '2', '2021-01-17 21:49:03', '2021-01-17 21:53:04', '1');
INSERT INTO `sys_menu` VALUES ('11', '2', '删除用户', null, 'sys:user:delete', null, '2', null, '3', '2021-01-17 21:49:21', null, '1');
INSERT INTO `sys_menu` VALUES ('12', '2', '分配角色', null, 'sys:user:role', null, '2', null, '4', '2021-01-17 21:49:58', null, '1');
INSERT INTO `sys_menu` VALUES ('13', '2', '重置密码', null, 'sys:user:repass', null, '2', null, '5', '2021-01-17 21:50:36', null, '1');
INSERT INTO `sys_menu` VALUES ('14', '3', '修改角色', null, 'sys:role:update', null, '2', null, '2', '2021-01-17 21:51:14', null, '1');
INSERT INTO `sys_menu` VALUES ('15', '3', '删除角色', null, 'sys:role:delete', null, '2', null, '3', '2021-01-17 21:51:39', null, '1');
INSERT INTO `sys_menu` VALUES ('16', '3', '分配权限', null, 'sys:role:perm', null, '2', null, '5', '2021-01-17 21:52:02', null, '1');
INSERT INTO `sys_menu` VALUES ('17', '4', '添加菜单', null, 'sys:menu:save', null, '2', null, '1', '2021-01-17 21:53:53', '2021-01-17 21:55:28', '1');
INSERT INTO `sys_menu` VALUES ('18', '4', '修改菜单', null, 'sys:menu:update', null, '2', null, '2', '2021-01-17 21:56:12', null, '1');
INSERT INTO `sys_menu` VALUES ('19', '4', '删除菜单', null, 'sys:menu:delete', null, '2', null, '3', '2021-01-17 21:56:36', null, '1');

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL,
  `code` varchar(64) NOT NULL,
  `remark` varchar(64) DEFAULT NULL COMMENT '备注',
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  `statu` int(5) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`) USING BTREE,
  UNIQUE KEY `code` (`code`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('3', '普通用户', 'normal', '只有基本查看功能', '2021-01-04 10:09:14', '2021-01-30 08:19:52', '1');
INSERT INTO `sys_role` VALUES ('6', '超级管理员', 'admin', '系统默认最高权限,不可以编辑和任意修改', '2021-01-16 13:29:03', '2021-01-17 15:50:45', '1');

-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_id` bigint(20) NOT NULL,
  `menu_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES ('60', '6', '1');
INSERT INTO `sys_role_menu` VALUES ('61', '6', '2');
INSERT INTO `sys_role_menu` VALUES ('62', '6', '9');
INSERT INTO `sys_role_menu` VALUES ('63', '6', '10');
INSERT INTO `sys_role_menu` VALUES ('64', '6', '11');
INSERT INTO `sys_role_menu` VALUES ('65', '6', '12');
INSERT INTO `sys_role_menu` VALUES ('66', '6', '13');
INSERT INTO `sys_role_menu` VALUES ('67', '6', '3');
INSERT INTO `sys_role_menu` VALUES ('68', '6', '7');
INSERT INTO `sys_role_menu` VALUES ('69', '6', '14');
INSERT INTO `sys_role_menu` VALUES ('70', '6', '15');
INSERT INTO `sys_role_menu` VALUES ('71', '6', '16');
INSERT INTO `sys_role_menu` VALUES ('72', '6', '4');
INSERT INTO `sys_role_menu` VALUES ('73', '6', '17');
INSERT INTO `sys_role_menu` VALUES ('74', '6', '18');
INSERT INTO `sys_role_menu` VALUES ('75', '6', '19');
INSERT INTO `sys_role_menu` VALUES ('76', '6', '5');
INSERT INTO `sys_role_menu` VALUES ('77', '6', '6');
INSERT INTO `sys_role_menu` VALUES ('96', '3', '1');
INSERT INTO `sys_role_menu` VALUES ('97', '3', '2');
INSERT INTO `sys_role_menu` VALUES ('98', '3', '3');
INSERT INTO `sys_role_menu` VALUES ('99', '3', '4');
INSERT INTO `sys_role_menu` VALUES ('100', '3', '5');
INSERT INTO `sys_role_menu` VALUES ('101', '3', '6');

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL,
  `avatar` varchar(255) DEFAULT NULL,
  `email` varchar(64) DEFAULT NULL,
  `city` varchar(64) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  `last_login` datetime DEFAULT NULL,
  `statu` int(5) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_USERNAME` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', '123@qq.com', '广州', '2021-01-12 22:13:53', '2021-01-16 16:57:32', '2020-12-30 08:38:37', '1');
INSERT INTO `sys_user` VALUES ('2', 'test', '$2a$10$0ilP4ZD1kLugYwLCs4pmb.ZT9cFqzOZTNaMiHxrBnVIQUGUwEvBIO', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', 'test@qq.com', null, '2021-01-30 08:20:22', '2021-01-30 08:55:57', null, '1');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('4', '1', '6');
INSERT INTO `sys_user_role` VALUES ('7', '1', '3');
INSERT INTO `sys_user_role` VALUES ('13', '2', '3');

发起刚刚的请求

4、获取用户接口信息

@GetMapping("/sys/userInfo")
    public Result userInfo(Principal principal){

        SysUser sysUser  = sysUserService.getByUserName(principal.getName());

        return Result.success(MapUtil.builder()
                .put("id",sysUser.getId())
                .put("username",sysUser.getUsername())
                .put("avatar",sysUser.getAvatar())
                .put("created",sysUser.getCreated())
                .map()
        );

    }

运行测试

http://localhost:8081/sys/userInfo

Authorization

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTY1NjU1ODY4NywiZXhwIjoxNjU3MTYzNDg3fQ.Nso0BloTp0eggRU2eyMJdkY27ZBow8OfoZWcHSk2USWNv9jtu1L_e9bvnBNmm_82hzr8JcpKFNE3U6wvI0m2xw

5、菜单的增删改查

List<SysMenu> tree();

@Override
    public List<SysMenu> tree() {

        //获取所有菜单信息
        List<SysMenu> sysMenus = this.list(new QueryWrapper<SysMenu>().orderByAsc("orderNum"));
        //转成树状结构
        return buildTreeMenu(sysMenus);
    }

添加校验信息的依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

在SysMenu添加校验规则

package cn.itbluebox.springbootadminvue.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 
 * </p>
 *
 * @author itbluebox
 * @since 2022-05-26
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class SysMenu extends BaseEntity {

    private static final long serialVersionUID = 1L;

    /**
     * 父菜单ID,一级菜单为0
     */
    @NotNull(message = "上级菜单不能为空")
    private Long parentId;

    @NotBlank(message = "菜单名称不能为空")
    private String name;
    /**
     * 菜单URL
     */
    private String path;
    /**
     * 授权(多个用逗号分隔,如:user:list,user:create)
     */
    @NotBlank(message = "菜单授权码不能为空")
    private String perms;

    private String component;
    /**
     * 类型     0:目录   1:菜单   2:按钮
     */
    @NotNull(message = "菜单类型不能为空")
    private Integer type;

    /**
     * 菜单图标
     */
    private String icon;

    /**
     * 排序
     */
    @TableField("orderNum")
    private Integer orderNum;

    @TableField(exist = false)
    private List<SysMenu> children = new ArrayList<>();

}

完善SysMenuController

@PostMapping("/delete/{id}")
    public Result delete(@PathVariable("id") Long id){

        int count = sysMenuService.count(new QueryWrapper<SysMenu>().eq("parent_id", id));

        if(count > 0){
            return Result.fail("请先删除子菜单");
        }
        sysUserService.clearUserAuthorityInfoByMenuId(id);
        sysMenuService.removeById(id);
        //同步删除中间表
        sysRoleMenuService.remove(new QueryWrapper<SysRoleMenu>().eq("menu_id",id));
        return Result.success("");
    }

运行测试

修改成功

继续添加

删除父级菜单

删除子菜单

删除成功

6、添加权限信息

@GetMapping("/info/{id}")
    @PreAuthorize("hasAnyAuthority('sys:menu:list')")
    public Result info(@PathVariable(name = "id") Long id){
        return Result.success(sysMenuService.getById(id));
    }

    @GetMapping("/list")
    @PreAuthorize("hasAnyAuthority('sys:menu:list')")
    public Result list(){

        List<SysMenu> menus = sysMenuService.tree();
        return Result.success(menus);

    }
    @PostMapping("/save")
    @PreAuthorize("hasAnyAuthority('sys:menu:save')")
    public Result save(@Validated @RequestBody SysMenu sysMenu){
        sysMenu.setCreated(LocalDateTime.now());
        sysMenuService.save(sysMenu);
        return Result.success(sysMenu);

    }
    @PostMapping("/update")
    @PreAuthorize("hasAnyAuthority('sys:menu:update')")
    public Result update(@Validated @RequestBody SysMenu sysMenu){
        sysMenu.setUpdated(LocalDateTime.now());
        sysMenuService.updateById(sysMenu);
        //清除所有与该菜单相关的缓存权限
        sysUserService.clearUserAuthorityInfoByMenuId(sysMenu.getId());
        return Result.success(sysMenu);
    }
    @PreAuthorize("hasAnyAuthority('sys:menu:delete')")
    @PostMapping("/delete/{id}")
    public Result delete(@PathVariable("id") Long id){

        int count = sysMenuService.count(new QueryWrapper<SysMenu>().eq("parent_id", id));

        if(count > 0){
            return Result.fail("请先删除子菜单");
        }
        sysUserService.clearUserAuthorityInfoByMenuId(id);
        sysMenuService.removeById(id);
        //同步删除中间表
        sysRoleMenuService.remove(new QueryWrapper<SysRoleMenu>().eq("menu_id",id));
        return Result.success("");
    }

五、角色接口开发

1、设置SysRole的校验信息

以及对应的内容

@Data
@EqualsAndHashCode(callSuper = true)
public class SysRole extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @NotBlank(message = "角色名称不能为空")
    private String name;

    @NotBlank(message = "角色编码不能为空")
    private String code;

    /**
     * 备注
     */
    private String remark;

    @TableField(exist = false)
    private List<Long> menuIds = new ArrayList<>();

}

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);
而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParamo
注:当同时使用@RequestParam ()和@RequestBody时,@RequestParam ()指定的参数可以是普通元素、
数组、集合、对象等等(即:当,@RequestBody与@RequestParam()可以同时使用时,原SpringMVC接收参数的机制不变,只不过RequestBody接收的是请求体里面的数据;而RequestParam接收的是key-value里面的参数,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)。

即:如果参数时放在请求体中, application/json传入后台的话,那么后台要用@RequestBody才能接收到;
如果不是放在请求体中的话,那么后台接收前台传过来的参数时,要用@RequestParam来接收,或则形参前什么也不写也能接收。

注:如果参数前写了@RequestParam(xx),那么前端必须有对应的xxx名字才行(不管其是否有值,当然可以通
过设置该注解的required属性来调节是否必须传),如果没有xxx名的话,那么请求会出错,报400。

注:如果参数前不写@RequestParam(xxx)的话,那么就前端可以有可以没有对应的xxx名字才行,如果有xxx名
的话,那么就会自动匹配;没有的话,请求也能正确发送。

追注:这里与feign消费服务时不同; feign消费服务时,如果参数前什么也不写,那么会被默认是
@RequestBody的。

通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中。

2、BaseController当中设置获取页码

/**
     * 获取页码
     */
    public Page getPage(){
        int current = ServletRequestUtils.getIntParameter(req,"current",1);
        int size = ServletRequestUtils.getIntParameter(req,"size",10);
        return new Page(current,size);

    }

3、设置状态

public final static Integer STATUS_ON = 0;

    public final static Integer STATUS_OFF = 1;

4、SysRoleController接口开发

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author itbluebox
 * @since 2022-05-26
 */
@RestController
@RequestMapping("/sys/role")
public class SysRoleController extends BaseController {

    @PreAuthorize("hasAuthority('sys:role:list')")
    @GetMapping("/info/{id}")
    public Result info(@PathVariable("id") Long id) {

        SysRole sysRole = sysRoleService.getById(id);

        // 获取角色相关联的菜单id
        List<SysRoleMenu> roleMenus = sysRoleMenuService.list(new QueryWrapper<SysRoleMenu>().eq("role_id", id));
        List<Long> menuIds = roleMenus.stream().map(p -> p.getMenuId()).collect(Collectors.toList());

        sysRole.setMenuIds(menuIds);
        return Result.success(sysRole);
    }

    @PreAuthorize("hasAuthority('sys:role:list')")
    @GetMapping("/list")
    public Result list(String name) {

        Page<SysRole> pageData = sysRoleService.page(getPage(),
                new QueryWrapper<SysRole>()
                        .like(StrUtil.isNotBlank(name), "name", name)
        );

        return Result.success(pageData);
    }

    @PostMapping("/save")
    @PreAuthorize("hasAuthority('sys:role:save')")
    public Result save(@Validated @RequestBody SysRole sysRole) {

        sysRole.setCreated(LocalDateTime.now());
        sysRole.setStatu(Const.STATUS_ON);

        sysRoleService.save(sysRole);
        return Result.success(sysRole);
    }

    @PostMapping("/update")
    @PreAuthorize("hasAuthority('sys:role:update')")
    public Result update(@Validated @RequestBody SysRole sysRole) {

        sysRole.setUpdated(LocalDateTime.now());

        sysRoleService.updateById(sysRole);

        // 更新缓存
        sysUserService.clearUserAuthorityInfoByRoleId(sysRole.getId());

        return Result.success(sysRole);
    }

    @PostMapping("/delete")
    @PreAuthorize("hasAuthority('sys:role:delete')")
    @Transactional
    public Result info(@RequestBody Long[] ids) {

        boolean flagRole = sysRoleService.removeByIds(Arrays.asList(ids));
        // 删除中间表
        boolean flagUserRole = sysUserRoleService.remove(new QueryWrapper<SysUserRole>().in("role_id", ids));
        boolean flagRoleMenu = sysRoleMenuService.remove(new QueryWrapper<SysRoleMenu>().in("role_id", ids));
        // 缓存同步删除
        Arrays.stream(ids).forEach(id -> {
            // 更新缓存
            sysUserService.clearUserAuthorityInfoByRoleId(id);
        });

        return Result.success(flagRole && flagUserRole && flagRoleMenu);
    }

    @Transactional
    @PostMapping("/perm/{roleId}")
    @PreAuthorize("hasAuthority('sys:role:perm')")
    public Result info(@PathVariable("roleId") Long roleId, @RequestBody Long[] menuIds) {

        List<SysRoleMenu> sysRoleMenus = new ArrayList<>();

        Arrays.stream(menuIds).forEach(menuId -> {
            SysRoleMenu roleMenu = new SysRoleMenu();
            roleMenu.setMenuId(menuId);
            roleMenu.setRoleId(roleId);

            sysRoleMenus.add(roleMenu);
        });

        // 先删除原来的记录,再保存新的
        sysRoleMenuService.remove(new QueryWrapper<SysRoleMenu>().eq("role_id", roleId));
        sysRoleMenuService.saveBatch(sysRoleMenus);

        // 删除缓存
        sysUserService.clearUserAuthorityInfoByRoleId(roleId);

        return Result.success(menuIds);
    }

}

完善一下SysRoleMenu

package cn.itbluebox.springbootadminvue.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 * 
 * </p>
 *
 * @author itbluebox
 * @since 2022-05-26
 */
@Data
public class SysRoleMenu{
    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private Long roleId;

    private Long menuId;

}

运行测试,新增角色信息

分配权限信息

查看

分配成功

修改成功

四、用户接口开发

用户管理里面有个用户关联角色的分配角色操作,
和角色关联菜单的写法差不多的,其他增删改查也复制黏贴改改就好,哈哈。

1、完善SysUser

@Data
@EqualsAndHashCode(callSuper = true)
public class SysUser extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @NotBlank(message = "用户名不能为空")
    private String username;

    private String password;

    private String avatar;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    private String city;

    private LocalDateTime lastLogin;

    @TableField(exist = false)
    private List<SysRole> sysRoles = new ArrayList<>();

}

2、完善SysUserController

@RestController
@RequestMapping("/sys/user")
public class SysUserController extends BaseController {

    @GetMapping("/info/{id}")
    public Result info(@PathVariable("id") Long id){
        SysUser sysUser = sysUserService.getById(id);
        Assert.notNull(sysUser,"找不到该管理员");

        List<SysRole> roles = sysRoleService.listRolesByUserId(id);

        sysUser.setSysRoles(roles);

        return Result.success(sysUser);
    }

    @GetMapping("/list")
    public Result list(){

        return Result.success();
    }

    @PostMapping("/save")
    public Result save(){

        return Result.success();
    }

    @PostMapping("/update")
    public Result update(){

        return Result.success();
    }

    @PostMapping("/role/{userId}")
    public Result rolePerm(){

        return Result.success();
    }

    @PostMapping("/repass")
    public Result repass(){

        return Result.success();
    }
}

3、完善SysRoleServiceImpl

@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Override
    public List<SysRole> listRolesByUserId(Long userId) {

        List<SysRole> sysRoleList =
                this.list(new QueryWrapper<SysRole>()
                        .inSql("id", "select role_id from sys_user_role where user_id = " + userId));

        return sysRoleList;
    }
}

完善对应的方法

public static final String  DEFAULT_PASSWORD = "888888";

    public static final String DEFAULT_AVATAR = "https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png";

4、完善SysUserController

@RestController
@RequestMapping("/sys/user")
public class SysUserController extends BaseController {

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @GetMapping("/info/{id}")
    @PreAuthorize("hasAnyAuthority('sys:user:list')")
    public Result info(@PathVariable("id") Long id){
        SysUser sysUser = sysUserService.getById(id);
        Assert.notNull(sysUser,"找不到该管理员");
        List<SysRole> roles = sysRoleService.listRolesByUserId(id);
        sysUser.setSysRoles(roles);
        return Result.success(sysUser);
    }

    @GetMapping("/list")
    @PreAuthorize("hasAnyAuthority('sys:user:list')")
    public Result list(String username){
        Page<SysUser> pageData = sysUserService.page(getPage(),
                new QueryWrapper<SysUser>()
                        .like(StrUtil.isNotBlank(username), "username", username)
        );
        pageData.getRecords().forEach(u ->{
            u.setSysRoles(sysRoleService.listRolesByUserId(u.getId()));
        });
        return Result.success(pageData);
    }

    @PostMapping("/save")
    @PreAuthorize("hasAnyAuthority('sys:user:save')")
    public Result save(@Validated @RequestBody SysUser sysUser){

        sysUser.setCreated(LocalDateTime.now());
        sysUser.setStatu(Const.STATUS_ON);

        //设置默认密码
        String password = passwordEncoder.encode(Const.DEFAULT_PASSWORD);
        sysUser.setPassword(password);
        //设置默认头像
        sysUser.setAvatar(Const.DEFAULT_AVATAR);
        sysUserService.save(sysUser);

        return Result.success(sysUser);
    }

    @PostMapping("/update")
    @PreAuthorize("hasAnyAuthority('sys:user:update')")
    public Result update(@Validated @RequestBody SysUser sysUser){
        sysUser.setUpdated(LocalDateTime.now());
        sysUserService.updateById(sysUser);
        return Result.success(sysUser);
    }

    @Transactional
    @PostMapping("/delete")
    @PreAuthorize("hasAnyAuthority('sys:user:delete')")
    public Result delete(@RequestBody Long[] ids){
        boolean flagSysUser = sysUserService.removeByIds(Arrays.asList(ids));
        //删除中间表
        boolean flagSysUserRole = sysUserRoleService.remove(new QueryWrapper<SysUserRole>().in("user_id", ids));
        return Result.success(flagSysUser && flagSysUserRole);
    }

    @Transactional
    @PostMapping("/role/{userId}")
    @PreAuthorize("hasAnyAuthority('sys:user:role')")
    public Result rolePerm(@PathVariable("userId") Long userId,@RequestBody Long[] roleIds){

        List<SysUserRole> userRoles = new ArrayList<>();
        Arrays.stream(roleIds).forEach(r ->{
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(r);
            sysUserRole.setUserId(userId);
            userRoles.add(sysUserRole);
        });
        boolean flagSysUserRole = sysUserRoleService.remove(new QueryWrapper<SysUserRole>().eq("user_id", userId));
        sysUserRoleService.saveBatch(userRoles);
        //删除缓存
        SysUser sysUser = sysUserService.getById(userId);
        sysUserService.clearUserAuthorityInfo(sysUser.getUsername());

        return Result.success(flagSysUserRole);
    }

    @PostMapping("/repass")
    @PreAuthorize("hasAnyAuthority('sys:user:repass')")
    public Result repass(@RequestBody Long userId){
        SysUser sysUser = sysUserService.getById(userId);
        sysUser.setPassword(passwordEncoder.encode(Const.DEFAULT_PASSWORD));
        sysUser.setUpdated(LocalDateTime.now());
        boolean flagSysUser = sysUserService.updateById(sysUser);
        return Result.success(flagSysUser);
    }
}

5、重新启动运行

删除成功

六、个人中心用户密码重置

1、创建PassDto

@Data
public class PassDto implements Serializable {

    @NotBlank(message = "新密码不能为空")
    private String password;

    @NotBlank(message = "旧密码不能为空")
    private String currentPass;
}

2、创建updatePass

@PostMapping("/updatePass")
    public Result updatePass(@Validated @RequestBody PassDto passDto, Principal principal){
        SysUser sysUser = sysUserService.getByUserName(principal.getName());
        boolean matches = passwordEncoder.matches(passDto.getCurrentPass(), sysUser.getPassword());
        if(!matches){
            return Result.fail("旧密码不正确");
        }
        //设置默认密码
        String password = passwordEncoder.encode(passDto.getPassword());
        sysUser.setPassword(password);
        //设置默认头像
        sysUser.setAvatar(Const.DEFAULT_AVATAR);
        sysUserService.updateById(sysUser);

        return Result.success(sysUser);
    }

3、完善UserCenter.vue

<template>
	<div style="text-align: center;">
		<h2>你好!{{ userInfo.username }} 同学</h2>

		<el-form :model="passForm" status-icon :rules="rules" ref="passForm" label-width="100px">
			<el-form-item label="旧密码" prop="currentPass">
				<el-input type="password" v-model="passForm.currentPass" autocomplete="off"></el-input>
			</el-form-item>
			<el-form-item label="新密码" prop="password">
				<el-input type="password" v-model="passForm.password" autocomplete="off"></el-input>
			</el-form-item>
			<el-form-item label="确认密码" prop="checkPass">
				<el-input type="password" v-model="passForm.checkPass" autocomplete="off"></el-input>
			</el-form-item>
			<el-form-item>
				<el-button type="primary" @click="submitForm('passForm')">提交</el-button>
				<el-button @click="resetForm('passForm')">重置</el-button>
			</el-form-item>
		</el-form>
	</div>
</template>

<script>

	export default {
		name: "Login",
		data() {
			var validatePass = (rule, value, callback) => {
				if (value === '') {
					callback(new Error('请再次输入密码'));
				} else if (value !== this.passForm.password) {
					callback(new Error('两次输入密码不一致!'));
				} else {
					callback();
				}
			};
			return {
				userInfo: {

				},
				passForm: {
					password: '',
					checkPass: '',
					currentPass: ''
				},
				rules: {
					password: [
						{ required: true, message: '请输入新密码', trigger: 'blur' },
						{ min: 6, max: 12, message: '长度在 6 到 12 个字符', trigger: 'blur' }
					],
					checkPass: [
						{ required: true, validator: validatePass, trigger: 'blur' }
					],
					currentPass: [
						{ required: true, message: '请输入当前密码', trigger: 'blur' },
					]
				}
			}
		},
		created() {
			this.getUserInfo()
		},
		methods: {
			getUserInfo() {
				this.$axios.get("/sys/userInfo").then(res => {

					this.userInfo = res.data.data;
				})
			},
			submitForm(formName) {
				this.$refs[formName].validate((valid) => {
					if (valid) {

						const _this = this
						this.$axios.post('/sys/user/updatePass', this.passForm).then(res => {

							_this.$alert(res.data.msg, '提示', {
								confirmButtonText: '确定',
								callback: action => {
									this.$refs[formName].resetFields();
								}
							});
						})

					} else {
						console.log('error submit!!');
						return false;
					}
				});
			},
			resetForm(formName) {
				this.$refs[formName].resetFields();
			}
		}
	}
</script>

<style scoped>
.el-form {
	width: 420px;
	margin: 50px auto;
}
</style>

4、启动运行测试

Java 之SpringBoot+Vue实现后台管理系统的开发【一、前端】跳转
Java 之SpringBoot+Vue实现后台管理系统的开发【二、后端】跳转
Java 之SpringBoot+Vue实现后台管理系统的开发【三、系统权限】跳转

相关文章