SpringBoot + interceptor + custom exception + custom annotation + global exception handling, simple interface permission management

SpringBoot + interceptor + custom exception + custom annotation + global exception handling, simple interface permission management

https://mp.weixin.qq.com/s/9C5gH28MQECT8n3NBbLdCQ

I think an interceptor plus other processing can meet the needs of the project. Why should I use another framework,

I divide permission management into three parts:

  1. Resource permissions: using URL as a resource, you can dynamically divide URL permissions for each account to access different URLs;
  2. Operation permissions: divide all URL s into four operation permissions: add, delete, modify and query, and assign corresponding operation permissions to users. If a user has only query operation permissions, he cannot do other operations;
  3. Role permissions: the system has multiple roles, and the permissions of each role are different. For example, a management background has an account management module and a commodity module. The role of super administrator can see and operate all modules, while the role of after-sales can only see the commodity module. Even if he knows the URL under the account management module, he has no permission to operate. Then, setting roles for users has corresponding permissions;

Resource permissions

1. Static resource preparation

It's convenient to demonstrate here, so you don't directly operate the database. The corresponding permission table relationship is also very simple. Here, you can directly establish the static URL relationship corresponding to the user.

public class Constant {
    /**
     * Authority management
     */
    public static Map<Integer,String[]> permission=new HashMap<>();
    static {
        //URL permissions owned by user 1
        String[] frist={"/url1","/url2","/url3","/url4","/url5","/url6","/url7"};
        
        //URL permissions owned by user 2
        String[] second={"/url1","/url2","/url3","/url4","/url5"};
        
        //URL permissions owned by user 3
        String[] third={"/url1","/url2","/url3"};
        
        permission.put(1,frist);
        permission.put(2,second);
        permission.put(3,third);

    }
}

2. Customize an exception to be intercepted and thrown

public class APIException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    
    //Abnormal message
    private String msg;
    
    //Construction assignment
    public APIException(String msg) {
        super(msg);
        this.msg = msg;
    }

}

3. Global exception handling

It is convenient to return unified and standardized results after exceptions. Normally, you should return custom Vo, but the goal here is to demonstrate the results. In addition, you should have your own Vo class, so you don't need it here

@RestControllerAdvice
public class WebExceptionControl {
    
    @ExceptionHandler(APIException.class)
    public String APIExceptionHandler(APIException e) {
        return e.getMessage();
    }
}

4.controller layer

It's easy to create the corresponding URL

@RestController
public class TestController {

    @RequestMapping(value = "/url1")
    public String url1() {
        return "Congratulations, you have this permission";
    }


    @RequestMapping(value = "/url7")
    public String url7() {
        return "Congratulations, you have this permission";
    }
}

5. Interceptor creation

Generally, you need to verify the token first, and then get the user's corresponding information according to the token. There is no login here, so you can directly pass the key value of the corresponding user in the token

/**
 * Rights management URL interceptor. Cannot public on class
 */
public class URLInterceptor implements HandlerInterceptor {

    // The preprocessing callback method uses true to represent release before the interface call, and false to represent no release
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //Forced to turn a lonely
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        //Get token
        String token = httpServletRequest.getHeader("token");
        //Gets the uri of the request
        String requestURL = httpServletRequest.getRequestURI();
        
        //1. Judge whether the user logs in according to the token
        if (token==null){   //Under normal circumstances, it is also necessary to judge whether it matches the token in redis
            // If there is no token or the tokens do not match, an exception will be thrown directly to prompt that you are not logged in
            throw  new APIException("The current user is not logged in");
        }
        
        //2. After successful login, obtain the URL permission set corresponding to the user according to the information in the user token. 1 2 3 get all permissions
        String[] strings = Constant.permission.get(Integer.valueOf(token));
        
        boolean hasPermission = false;
        //3. Compare the URL set corresponding to the user with the URL of the current request. If there is a match, it will be released. Otherwise, an exception will be thrown
        for (int i =0;i<strings.length;i++) { //ergodic
            if (strings[i].equals(requestURL)){//If the same requestURL
                //Permission feasible
                hasPermission = true;
                break;
            }
        }
        if (hasPermission){
            return true;
        }else {
            throw  new APIException("The current user has no access path" + requestURL + "Permissions for");
        }
    }
}

6. Inject the interceptor into the application

@Configuration
public class WebMvcConfg implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //Permission interceptor
        registry.addInterceptor(urlInterceptor()).addPathPatterns("/**");
    }
    @Bean
    public URLInterceptor urlInterceptor(){
        return new URLInterceptor();
    }
}

7. Verify and view the results

Pass in the key s of users 1-3 in the token respectively. You can see the effect by visiting different URLs, as shown below

Replace the token with 1 to display:

Congratulations, you have this permission

Operation authority

1. Use static resources as above

public class Constant {
    /**
     * Authority management
     */
    public static Map<Integer,String[]> permission=new HashMap<>();
    static {
        String[] frist={"insert","delete","select","update"};//Operation permissions owned by user 1
        String[] second={"insert","select","update"};//URL permissions owned by user 2
        String[] third={"select"};//URL permissions owned by user 3
        permission.put(1,frist);
        permission.put(2,second);
        permission.put(3,third);

    }
}

2. User defined annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlAnnotation {
    /**
     * Operation type: add, delete, modify, insert
     * 
     */
    String type();
}

3.controller layer

It is marked with user-defined annotation to represent the operation of the corresponding method

4. Modify the previous interceptor

Add the acquisition of the operation type above the annotation, and use the type type to compare and judge

		//Strong rotation
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //Acquisition method
        Method method = handlerMethod.getMethod();
        //Get action annotation
        UrlAnnotation urlAnnotation = method.getAnnotation(UrlAnnotation.class);
        //If the obtained is null
        if (null == urlAnnotation) {
            throw new APIException("The current user has no access path" + requestURL + "Permissions for");
        }
		//@UrlAnnotation(type = "select") gets the type
        String type = urlAnnotation.type();


        //2. After successful login, obtain the URL permission set corresponding to the user according to the information in the user token. 1 2 3 get all permissions
        String[] strings2 = Constant.permissionCRUD.get(Integer.valueOf(token));

        boolean hasPermission2 = false;

        //If you have permission on action in the array, you can access it
        for (int i = 0; i < strings2.length; i++) { 
            if (strings2[i].equals(type)) {
                //Permission feasible
                hasPermission2 = true;
                break;
            }
        }

5. Verification results

Everything else remains the same

http://localhost:8080/url1 . token=2, 2 users, with query permission. Interface 1 is a query

Congratulations, you have this permission

Role permissions

In fact, the operations here are similar to those above. Let me briefly demonstrate here, taking two modules as examples

1. Static correspondence

public class Constant {
    /**
     * Authority management
     */
    public static Map<Integer,String[]> permission=new HashMap<>();
    static {
        String[] frist={"test","test1"};//Module permissions owned by user 1. Test here is the module URL entry. For example, all URLs under the test module are / test/**
        
        String[] second={"test"};//Module permissions owned by user 2
        
        permission.put(1,frist); //1 here is actually the role with user ID 1
        
        permission.put(2,second);//This 2 is the same

    }

2. Modify controller

Simulate two modules

3. Modify the interceptor

        String[] split = requestURL.split("/");

        //Intercept the first one. The 0th bit '', 1 is test and 2 is url1
        if (split[1] == null) {
            throw new APIException("url format error");
        }
        String[] strings3 = Constant.permissionResource.get(Integer.valueOf(token));

        boolean hasResource = false;

        for (int i = 0; i < strings3.length; i++) {
            if (strings3[i].equals(split[1])) {
                hasResource = true;
                break;
            }
        }

4. Test results

http://localhost:8080/test/url1

token = 2,2 User has test Permission, appear: Congratulations, you have this permission

summary

In fact, using a custom interceptor to do permissions, no matter what type it is, it is basically the same. The most important thing is to deal with it flexibly according to the needs. The above static permissions can be modified by establishing a role table, user table, path table and corresponding relationship. It is easy to configure the dynamic permissions of the URL. The flexible use of custom annotation can be more detailed.

Keywords: Spring Boot

Added by ramesh_iridium on Thu, 13 Jan 2022 16:48:38 +0200