Spring boot plus v1.4.0 release integration user role authority department management

RBAC user role permissions

User role authority department management core interface introduction

Shiro permission configuration

Database model diagram

Get verification code

  • Can be configured to enable the verification code
  • Default not enabled
  • If verification code verification is enabled, you need to enter verifyToken and code when logging in

Verification code demonstration

spring-boot-plus:
  # Whether to enable ansi console to output colored fonts
  enable-ansi: true
  # Enable verification code or not
  enable-verify-code: true

Enable verify code set to true enable verification code verification

Two ways to get the verification code

The verification code is saved in Redis in the background, and the expiration time is 5 minutes by default

Method 1:

Output the picture to the browser, and output the verification code token to the response header

http://localhost:8888/verificationCode/getImage

Response Headers
HTTP/1.1 200
verifyToken: 6515b4b798ce49e68b1e40f98ff8eb19
Mode two:

Get Base64 encoded picture and verification code token

http://localhost:8888/verificationCode/getBase64Image
{
  "code": 200,
  "msg": "Successful operation",
  "success": true,
  "data": {
    "image": "data:image/png;base64,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",
    "verifyToken": "42ba8abde7bc47b2b1397b4d6676956a"
  },
  "time": "2019-11-01 22:40:37"
}

System user login

  • POST request, content type: application / JSON
http://127.0.0.1:8888/login
  • Request parameters
{
  "code": "Verification Code",
  "password": "123456",
  "username": "admin",
  "verifyToken": "Verification Code token"
}

Be careful

  • If login is not enabled, just pass in username and password
  • The front end shall encrypt the password before transmission

Landing successfully

  • Return login user information: Department / role / authority
  • Return user token
{
  "code": 200,
  "msg": "Landing successfully",
  "success": true,
  "data": {
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3ZWIiLCJpc3MiOiJzcHJpbmctYm9vdC1wbHVzIiwiZXhwIjoxNTcyNjIzMDE5LCJpYXQiOjE1NzI2MTk0MTksImp0aSI6IjdlZmVlM2UwMjc2MTRiYTc5M2I2YmYwZmE4NTgzYmUwIiwidXNlcm5hbWUiOiJhZG1pbiJ9.O3w7CNRqw_Miwp8MDzPND6w490c9Q7yFlKpFJK9ubSU",
    "loginSysUserVo": {
      "id": "1",
      "username": "admin",
      "nickname": "Administrators",
      "gender": 1,
      "state": 1,
      "departmentId": "1",
      "departmentName": "Management Department",
      "roleId": "1",
      "roleName": "Administrators",
      "roleCode": "admin",
      "permissionCodes": [
        "sys:permission:codes",
        "system:management",
        "sys:department:update",
        "sys:department:page",
        "sys:role:management",
        "sys:permission:add",
        "sys:user:add",
        "sys:role:page",
        "sys:permission:page",
        "sys:department:delete",
        "sys:permission:management",
        "sys:user:delete",
        "sys:department:management",
        "sys:user:page",
        "sys:user:update",
        "sys:user:update:password",
        "sys:user:update:head",
        "sys:role:add",
        "sys:permission:menu:tree",
        "sys:department:info",
        "sys:permission:all:menu:list",
        "sys:permission:info",
        "sys:role:info",
        "sys:permission:all:menu:tree",
        "sys:permission:update",
        "sys:permission:menu:list",
        "sys:role:update",
        "sys:user:info",
        "sys:user:management",
        "sys:role:delete",
        "sys:permission:delete"
      ]
    }
  },
  "time": "2019-11-01 22:43:39"
}
  • The default expiration time of token is 1 hour
  • Set JWT Token expiration time
  ############################ JWT start #############################
  jwt:
    # Default expiration time 1 hour, unit: Second
    expire-second: 3600
  • Background uses Redis to cache login user information
  • redis key
login:user:admin
For other interfaces requiring authorization, the request header must carry token

Department tree list

  • The Department can be set to level N, and the background uses recursion to convert the Department list to a tree list

  • SysDepartmentServiceImpl
    @Override
    public List<SysDepartmentTreeVo> getAllDepartmentTree() {
        List<SysDepartment> sysDepartmentList = getAllDepartmentList();
        if (CollectionUtils.isEmpty(sysDepartmentList)) {
            throw new IllegalArgumentException("SysDepartment List cannot be empty");
        }
        List<SysDepartmentTreeVo> list = SysDepartmentConvert.INSTANCE.listToTreeVoList(sysDepartmentList);
        List<SysDepartmentTreeVo> treeVos = new ArrayList<>();
        for (SysDepartmentTreeVo treeVo : list) {
            if (treeVo.getParentId() == null) {
                treeVos.add(findChildren(treeVo, list));
            }
        }
        return treeVos;
    }

    /**
     * Get tree result list recursively
     *
     * @param tree
     * @param list
     * @return
     */
    public SysDepartmentTreeVo findChildren(SysDepartmentTreeVo tree, List<SysDepartmentTreeVo> list) {
        for (SysDepartmentTreeVo vo : list) {
            if (tree.getId().equals(vo.getParentId())) {
                if (tree.getChildren() == null) {
                    tree.setChildren(new ArrayList<>());
                }
                tree.getChildren().add(findChildren(vo, list));
            }
        }
        return tree;
    }
  • Front end JSON structure
http://127.0.0.1:8888/sysDepartment/getAllDepartmentTree

Role management

Set role permissions

  • Core code, delete role permission, add role permission
  • Finding the difference set of a set
  • SysRolePermissionServiceImpl
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateSysRole(UpdateSysRoleParam updateSysRoleParam) throws Exception {
        Long roleId = updateSysRoleParam.getId();
        List<Long> permissionIds = updateSysRoleParam.getPermissionIds();
        // Verify role exists
        SysRole sysRole = getById(roleId);
        if (sysRole == null) {
            throw new BusinessException("The role does not exist");
        }
        // Verify whether the permission list exists
        if (!sysPermissionService.isExistsByPermissionIds(permissionIds)) {
            throw new BusinessException("Permission list id Matching failure");
        }

        // Modifying roles
        sysRole.setName(updateSysRoleParam.getName())
                .setType(updateSysRoleParam.getType())
                .setRemark(updateSysRoleParam.getRemark())
                .setState(updateSysRoleParam.getState())
                .setUpdateTime(new Date());
        boolean updateResult = updateById(sysRole);
        if (!updateResult) {
            throw new DaoException("Failed to modify system role");
        }

        // Get previous permission id set
        List<Long> beforeList = sysRolePermissionService.getPermissionIdsByRoleId(roleId);
        // Difference set calculation
        // before: 1,2,3,4,5,6
        // after:  1,2,3,4,7,8
        // Delete 5,6 add 7,8
        // Remove @ TableLogic annotation of deleted field
        Set<Long> beforeSet = new HashSet<>(beforeList);
        Set<Long> afterSet = new HashSet<>(permissionIds);
        SetUtils.SetView deleteSet = SetUtils.difference(beforeSet, afterSet);
        SetUtils.SetView addSet = SetUtils.difference(afterSet, beforeSet);
        log.debug("deleteSet = " + deleteSet);
        log.debug("addSet = " + addSet);

        // Delete permission Association
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.eq("role_id",roleId);
        updateWrapper.in("permission_id",deleteSet);
        boolean deleteResult = sysRolePermissionService.remove(updateWrapper);
        if (!deleteResult) {
            throw new DaoException("Failed to delete role permission relationship");
        }
        // New permission Association
        boolean addResult = sysRolePermissionService.saveSysRolePermissionBatch(roleId, addSet);
        if (!addResult) {
            throw new DaoException("Failed to add role permission relationship");
        }
        return true;
    }

Privilege management

Permission tree list

  • When the user sets the role permission, select the permission menu
  • Permissions are divided into menu and function permissions
  • Get the three-tier permission tree in the background
    @Override
    public List<SysPermissionTreeVo> getAllMenuTree() throws Exception {
        List<SysPermission> list = getAllMenuList();
        // Convert to tree menu
        List<SysPermissionTreeVo> treeVos = convertSysPermissionTreeVoList(list);
        return treeVos;
    }

    @Override
    public List<SysPermissionTreeVo> convertSysPermissionTreeVoList(List<SysPermission> list) {
        if (CollectionUtils.isEmpty(list)) {
            throw new IllegalArgumentException("SysPermission List cannot be empty");
        }
        // Get map by level group
        Map<Integer, List<SysPermission>> map = list.stream().collect(Collectors.groupingBy(SysPermission::getLevel));
        List<SysPermissionTreeVo> treeVos = new ArrayList<>();
        // Loop to get the three-level menu tree set
        for (SysPermission one : map.get(LevelEnum.ONE.getKey())) {
            SysPermissionTreeVo oneVo = SysPermissionConvert.INSTANCE.permissionToTreeVo(one);
            Long oneParentId = oneVo.getParentId();
            if (oneParentId == null || oneParentId == 0) {
                treeVos.add(oneVo);
            }
            List<SysPermission> twoList = map.get(LevelEnum.TWO.getKey());
            if (CollectionUtils.isNotEmpty(twoList)) {
                for (SysPermission two : twoList) {
                    SysPermissionTreeVo twoVo = SysPermissionConvert.INSTANCE.permissionToTreeVo(two);
                    if (two.getParentId().equals(one.getId())) {
                        oneVo.getChildren().add(twoVo);
                    }
                    List<SysPermission> threeList = map.get(LevelEnum.THREE.getKey());
                    if (CollectionUtils.isNotEmpty(threeList)) {
                        for (SysPermission three : threeList) {
                            if (three.getParentId().equals(two.getId())) {
                                SysPermissionTreeVo threeVo = SysPermissionConvert.INSTANCE.permissionToTreeVo(three);
                                twoVo.getChildren().add(threeVo);
                            }
                        }
                    }
                }
            }

        }
        return treeVos;
    }
  • Front end JSON format
http://127.0.0.1:8888/sysPermission/getAllMenuTree

Permission code list

Return all the authority codes of the current user for the convenience of displaying the navigation menu and function buttons on the front end

http://127.0.0.1:8888/sysPermission/getPermissionCodesByUserId/1
{
  "code": 200,
  "msg": "Successful operation",
  "success": true,
  "data": [
    "system:management",
    "system:management",
    "sys:user:management",
    "sys:user:management",
    "sys:role:management",
    "sys:permission:management",
    "sys:department:management",
    "sys:user:add",
    "sys:user:add",
    "sys:user:update",
    "sys:user:update",
    "sys:user:delete",
    "sys:user:delete",
    "sys:user:info",
    "sys:user:info",
    "sys:user:page",
    "sys:user:page",
    "sys:user:update:password",
    "sys:user:update:head",
    "sys:role:add",
    "sys:role:update",
    "sys:role:delete",
    "sys:role:info",
    "sys:role:page",
    "sys:permission:add",
    "sys:permission:update",
    "sys:permission:delete",
    "sys:permission:info",
    "sys:permission:page",
    "sys:permission:all:menu:list",
    "sys:permission:all:menu:tree",
    "sys:permission:menu:list",
    "sys:permission:menu:tree",
    "sys:permission:codes",
    "sys:department:update",
    "sys:department:delete",
    "sys:department:info",
    "sys:department:page"
  ],
  "time": "2019-11-02 00:32:17"
}

Be careful

  • Use Shiro annotation @ RequiresPermissions to filter the permissions of the controller method
@RequiresPermissions("sys:department:add")
  • When generating code, you can configure to generate the RequiresPermissions annotation
        // Generate Shiro RequiresPermissions annotation
        codeGenerator.setRequiresPermissions(true);
  • To generate or add a controller method, you need to perform permission management. You need to add a permission code record to the sys permission table and give permissions to the corresponding roles

Tags: Programming Shiro Redis JSON Spring

Posted on Mon, 04 Nov 2019 09:13:18 -0800 by batfastad