The user-defined annotation implements RBAC permission verification. It's no longer said that you won't

catalogue

1. Foreword

2. Realization idea

3. Coding practice

3.1 preparation

3.2 preparation of database table

3.3. User defined annotation

3.4 interceptor

3.5 interface usage

3.6 test

3.7 conclusion

4. Conclusion

1. Foreword

Everyone who has studied Spring Security knows that the SpringBoot project can integrate Spring Security as a permission verification framework, and then directly use the @ PreAuthorize annotation on the Controller interface to verify permissions. However, what should I do if I don't want to introduce third-party frameworks such as Security and Shiro to achieve the effect of permission verification?

Next, we will introduce a scheme: interceptor + custom annotation for permission verification based on RBAC model

2. Realization idea

  1. Firstly, the database needs tables based on RBAC model. Here we use the simplest five tables as examples: user table, role table, permission table, user role association table and role permission association table

  2. After each successful login, the user needs to query the permission set string owned by the user in the associated table, encrypt it into the token string (JWT can be used here), and return it to the front end, which will be stored in the browser. In the future, each time the background interface is requested, the token will be taken out, put into the request header information and brought to the background

  3. Then create a new user-defined annotation, which is used on the interface, indicating that the interface needs to verify the permissions of visitors

  4. Create a new interceptor to intercept the requests of all request methods, and then judge whether there is a user-defined annotation. If so, take out the permission string and compare it with the permission string set of token in the request header. If it is included, it means that the user has permission to access the method. If it is not included, it means that the user does not have permission to access the method

3. Coding practice

3.1 preparation

Here I have prepared a SpringBoot basic environment code, which I will integrate on this basis. I have uploaded the code, and the address is as follows:

SpringBootBase: SpringBoot foundation project framework

Students with weak foundation can download it. Follow my steps step by step, and you can get the effect, and then integrate it into your own project

3.2 preparation of database table

Preparation of 5 sheets:

User table:

CREATE TABLE `sys_user` (
  `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'user ID',
  `dept_id` bigint(20) DEFAULT NULL COMMENT 'department ID',
  `position_id` varchar(5) DEFAULT NULL COMMENT 'position ID',
  `class_id` bigint(20) DEFAULT NULL COMMENT 'class ID',
  `user_name` varchar(30) NOT NULL COMMENT 'User account',
  `nick_name` varchar(30) NOT NULL COMMENT 'User nickname',
  `user_type` varchar(2) DEFAULT '00' COMMENT 'User type (00 system user)',
  `email` varchar(50) DEFAULT '' COMMENT 'User mailbox',
  `phonenumber` varchar(11) DEFAULT '' COMMENT 'phone number',
  `sex` char(1) DEFAULT '0' COMMENT 'User gender (0 male 1 female 2 unknown)',
  `avatar` varchar(300) DEFAULT '' COMMENT 'Avatar address',
  `password` varchar(100) DEFAULT '' COMMENT 'password',
  `status` char(1) DEFAULT '0' COMMENT 'Account status (0 normal 1 disabled)',
  `del_flag` char(1) DEFAULT '0' COMMENT 'Delete flag (0 for existence and 2 for deletion)',
  `login_ip` varchar(128) DEFAULT '' COMMENT 'Last login IP',
  `login_date` datetime DEFAULT NULL COMMENT 'Last login time',
  `create_by` varchar(64) DEFAULT '' COMMENT 'creator',
  `create_time` datetime DEFAULT NULL COMMENT 'Creation time',
  `update_by` varchar(64) DEFAULT '' COMMENT 'Updater',
  `update_time` datetime DEFAULT NULL COMMENT 'Update time',
  `remark` varchar(500) DEFAULT NULL COMMENT 'remarks',
  PRIMARY KEY (`user_id`),
  UNIQUE KEY `un_user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='User information table';

Role table:

CREATE TABLE `sys_role` (
  `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'role ID',
  `role_name` varchar(30) NOT NULL COMMENT 'Role name',
  `role_key` varchar(100) DEFAULT NULL COMMENT 'Role permission string',
  `role_sort` int(4) DEFAULT NULL COMMENT 'Display order',
  `status` char(1) NOT NULL DEFAULT '0' COMMENT 'Role status (0 normal 1 disabled)',
  `del_flag` char(1) DEFAULT '0' COMMENT 'Delete flag (0 for existence and 2 for deletion)',
  `create_by` varchar(64) DEFAULT '' COMMENT 'creator',
  `create_time` datetime DEFAULT NULL COMMENT 'Creation time',
  `update_by` varchar(64) DEFAULT '' COMMENT 'Updater',
  `update_time` datetime DEFAULT NULL COMMENT 'Update time',
  `remark` varchar(500) DEFAULT NULL COMMENT 'remarks',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Role information table';

Permission table:

CREATE TABLE `sys_menu` (
  `menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'menu ID',
  `menu_name` varchar(50) NOT NULL COMMENT 'Menu name',
  `parent_id` bigint(20) DEFAULT '0' COMMENT 'Parent menu ID',
  `order_num` int(4) DEFAULT '0' COMMENT 'Display order',
  `path` varchar(200) DEFAULT '' COMMENT 'menu_id Full path',
  `component` varchar(255) DEFAULT NULL COMMENT 'Component path',
  `query` varchar(255) DEFAULT NULL COMMENT 'Routing parameters',
  `is_frame` int(1) DEFAULT '1' COMMENT 'External chain (0 yes, 1 no)',
  `is_cache` int(1) DEFAULT '0' COMMENT 'Whether to cache (0 cache 1 do not cache)',
  `menu_type` char(1) DEFAULT '' COMMENT 'Menu type( M catalogue C menu F Button)',
  `visible` char(1) DEFAULT '0' COMMENT 'Menu status (0 show 1 hide)',
  `status` char(1) DEFAULT '0' COMMENT 'Menu status (0 normal 1 disabled)',
  `perms` varchar(100) DEFAULT NULL COMMENT 'Permission ID',
  `icon` varchar(100) DEFAULT '#'COMMENT' menu icon ',
  `create_by` varchar(64) DEFAULT '' COMMENT 'creator',
  `create_time` datetime DEFAULT NULL COMMENT 'Creation time',
  `update_by` varchar(64) DEFAULT '' COMMENT 'Updater',
  `update_time` datetime DEFAULT NULL COMMENT 'Update time',
  `remark` varchar(500) DEFAULT '' COMMENT 'remarks',
  PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Menu permission table';

User role association table:

CREATE TABLE `sys_user_role` (
  `user_id` bigint(20) NOT NULL COMMENT 'user ID',
  `role_id` bigint(20) NOT NULL COMMENT 'role ID',
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='User and role association table';

Role permission association table:

CREATE TABLE `sys_role_menu` (
  `role_id` bigint(20) NOT NULL COMMENT 'role ID',
  `menu_id` bigint(20) NOT NULL COMMENT 'menu ID',
  PRIMARY KEY (`role_id`,`menu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Role and menu association table';

3 table associated query. You can query the permission strings of the user by passing in the user ID. the SQL is as follows:

select * from sys_menu t1
left join sys_role_menu t2 on t1.menu_id = t2.menu_id
left join sys_user_role t3 on t2.role_id = t3.role_id
where t3.user_id = #{userId}

3.3. User defined annotation

Create a new custom annotation:

package org.wujiangbo.annotation;

import java.lang.annotation.*;

/**
 * Custom annotation verification permission
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {

    /**
     * Permission string
     */
    String per() default "";
}

3.4 interceptor

Create a new interceptor with the following code:

package org.wujiangbo.interceptor;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.wujiangbo.annotation.CheckPermission;
import org.wujiangbo.exception.MyException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Permission verification interceptor
 */
@Component
public class CheckPermissionInterceptor implements HandlerInterceptor {

    /**
     * Preprocessing
     * This method will be called before the request is processed
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        if (!(handler instanceof HandlerMethod)) {
            //Explain that if the intercepted request is not the request method, it will be released directly
            return true;
        }
        //Specify here to indicate that the requested method is
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //Get method object
        Method method = handlerMethod.getMethod();
        //Get the CheckPermission annotation object above the method
        CheckPermission methodAnnotation = method.getAnnotation(CheckPermission.class);
        if (methodAnnotation != null) {
            //Get permission string
            String per = methodAnnotation.per();
            if(StringUtils.isNotBlank(per)){
                /**
                 * Description: you need to verify the permission. You can access the method only if you have the permission string
                 * At this time, you need to get the token from the request header information, decrypt the token, get the user's permission set, and then make a judgment
                 */
                //Here we create some test data, assuming that the permission set of the currently logged in user is parsed from the token as follows
                List<String> userPermissionList = new ArrayList<>();
                userPermissionList.add("user:addUser");
                userPermissionList.add("user:deleteUser");
                userPermissionList.add("user:updateUser");
                userPermissionList.add("user:pageList");

                //Start judging
                if(!userPermissionList.contains(per)){
                    throw new MyException("You do not have permission to perform this operation");
                }
            }
        }
        //You must return true, otherwise all requests will be intercepted and the contents in the controller method will not be executed
        return true;
    }
}

You need to add the above interceptor class to the web environment, and create the following configuration class:

package org.wujiangbo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.wujiangbo.interceptor.CheckPermissionInterceptor;

/**
 * Unified interceptor configuration class
 */
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Autowired
    private CheckPermissionInterceptor checkPermissionInterceptor;

    //Add custom interceptor
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(checkPermissionInterceptor).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

be careful:

In the interceptor here, I did not parse the token, but just simulated it. In the actual project, the token must be ciphertext and cannot be used directly. You need to parse the permission set string owned by the user first

3.5 interface usage

Now we can use it directly in the controller interface, as follows:

package org.wujiangbo.controller;

import lombok.extern.slf4j.Slf4j;
import org.wujiangbo.annotation.CheckPermission;
import org.wujiangbo.result.JSONResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Test interface class
 */
@RestController
@Slf4j
public class TestController {

    //User added interface
    @GetMapping("/user/addUser")
    @CheckPermission(per = "user:addUser") //Indicates that the visitor needs user:addUser permission to access the interface
    public JSONResult addUser(){
        return JSONResult.success("addUser success");
    }

    //Department new interface
    @GetMapping("/dept/addDept")
    @CheckPermission(per = "dept:addDept") //Indicates that the visitor needs to have dept:addDept permission to access this interface
    public JSONResult addDept(){
        return JSONResult.success("addDept success");
    }

}

3.6 test

Test with postman tool. First access the new user interface: http://localhost:8001/user/addUser

 

Then access the new Department interface: http://localhost:8001/dept/addDept

 

3.7 conclusion

It can be seen from the test results that the permission verification of the interface can indeed be achieved. If it is used in the future, it can be directly annotated in the controller, which is very convenient

4. Conclusion

  • Such permission verification also has application scenarios in practical work. I hope everyone can master it

  • If you have any questions, you can leave a message and I will reply as soon as possible

  • Finally, don't forget to praise. Thank you very much for your support and love. I will continue to share more dry goods

Keywords: Java Spring Spring Boot Back-end

Added by asmon on Sat, 22 Jan 2022 20:37:03 +0200