SpringBoot foundation of microservice series: filter, interceptor and Aop aspect

Posted by apw on Wed, 02 Mar 2022 12:38:33 +0100

SpringBoot filter, interceptor, Aop section

1, Filter

Add the following annotation to the startup class: @ ServletComponentScan.
Filter is a Servlet container level filter. It can filter almost all requests based on function callback. The complete process of filter: filter preprocesses the user request, then sends the request to the Servlet for preprocessing and generates a response, and finally filter post processes the server response. The filter implementation example is as follows:

package com.example.demo01.filter;

import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;

@WebFilter(filterName = "SessionFilter",
        displayName = "SessionFilter",
        urlPatterns = {"/*"},
        initParams = @WebInitParam(
                name = "SessionFilterInitParam",
                value = "SessionFilter")
)
public class SessionFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper((HttpServletRequest) servletRequest) {

            @Override
            public String getHeader(String name) {
                String superHeader = super.getHeader(name);
                if ("test".equals(name) && StringUtils.isEmpty(superHeader)) {
                    String session = servletRequest.getParameter("session");
                    if (!StringUtils.isEmpty(session)) {
                        return session;
                    }
                }
                return superHeader;
            }

        };
        filterChain.doFilter(requestWrapper, servletResponse);
    }
}

2, Interceptor

Interceptor interceptor is essentially different from filter. Filter depends on Servlet container, while interceptor depends on Spring framework. It is a manifestation of AOP and implemented by Java based dynamic agent. Interceptors are used in AOP to intercept a method or field before it is accessed, and then add some operations before or after it. Interception is an implementation strategy of AOP.

Interceptor function:
Log record: record the log of request information;
Permission check: for example, login detection, enter the processor to detect whether to log in;
Performance monitoring: the interceptor records the start time before entering the processor and the end time after processing, so as to obtain the processing time of the request.

Interceptor implementation
Implement the HandlerInterceptor interface and rewrite the method of the interface to realize user-defined interception. How to implement interceptors in SpringBoot:
Class declaring Interceptor:
By implementing the HandlerInterceptor interface, preHandle, postHandle and afterCompletion methods are implemented.
Configuration Interceptor:
The addInterceptors method is implemented by implementing the WebMvcConfigurer interface. The following example code:

@Slf4j
public class SignAuthInterceptor implements HandlerInterceptor {
    /**
     * 5 Minute validity
     */
    private final static long MAX_EXPIRE = 5 * 60;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("request URI = " + request.getRequestURI());
        HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
        //Get all parameters (including URL and on the body)
        SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
        //Signature verification of parameters
        String headerSign = request.getHeader(CommonConstant.X_SIGN);
        String timesTamp = request.getHeader(CommonConstant.X_TIMESTAMP);

        //1. There is a message about the verification time
        try {
            DateUtils.parseDate(timesTamp, "yyyyMMddHHmmss");
        } catch (Exception e) {
            throw new IllegalArgumentException("Signature verification failed:X-TIMESTAMP Format must be:yyyyMMddHHmmss");
        }
        Long clientTimestamp = Long.parseLong(timesTamp);
        //Judgment timestamp = 2018091113
        if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) {
            throw new IllegalArgumentException("Signature verification failed:X-TIMESTAMP Expired ");
        }

        //2. Verification signature
        boolean isSigned = SignUtil.verifySign(allParams,headerSign);

        if (isSigned) {
            log.debug("Sign Signature passed! Header Sign : {}",headerSign);
            return true;
        } else {
            log.error("request URI = " + request.getRequestURI());
            log.error("Sign Signature verification failed! Header Sign : {}",headerSign);
            //If the verification fails, return to the front end
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            PrintWriter out = response.getWriter();
            Result<?> result = Result.error("Sign Signature verification failed!");
            out.print(JSON.toJSON(result));
            return false;
        }
    }
/**
 * Signature interceptor configuration
 */
@Configuration
public class SignAuthConfiguration implements WebMvcConfigurer {
    public static String[] urlList = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*","/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
    @Bean
    public SignAuthInterceptor signAuthInterceptor() {
        return new SignAuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(signAuthInterceptor()).addPathPatterns(urlList);
    }
}

3, Spring aop aspect

Compared with interceptors, Spring's aop is more powerful and encapsulated in more detail.
The notes involved in Aop are as follows:

@Aspect: define a java class as an aspect class.
@Pointcut: defines a pointcut, which can be a regular expression, such as all functions under a package in the following example, or an annotation.
@Before: cut into the content at the beginning of the entry point.
@After: cut in content at the end of the pointcut.
@After returning: processing logic after the pointcut return content.
@Around: cut in the content before and after the pointcut, and control when to execute the content of the pointcut itself. In principle, it can be replaced
@Before and @ After.
@After throwing: used to handle the processing logic after an exception is thrown in the cut in content part.
@Order(100): the execution order of AOP section. The smaller the @ Before value, the earlier the execution, @ After and @ AfterReturning
The larger the value, the earlier the execution.

Introduce dependency

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

The example code is as follows:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;

@Aspect
@Slf4j
@Component
public class MethodMonitorAspect {

    private long startTime;

    //Declare the aspect class path. The type must be final String. The variables to be used in the annotation can only be static constant types
    //public static final String POINT = "execution(* com.product.service.*.*(..))";
    //You can also use annotations to declare pointcuts, as follows
    @Pointcut("execution(* com.liuc.server.api.sechdule.invoice.*.*(..))")
    public void point() {
    }

    @Before("point()")
    public void doBefore(JoinPoint pj) {
        this.startTime = System.currentTimeMillis();

        Object target = pj.getTarget();
        String className = target.getClass().getName();  //The class and package to which the currently executed method belongs

        MethodSignature signature = (MethodSignature) pj.getSignature();
        String methodName = signature.getName();  //Name of the currently executed method
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        log.info("class{}.{}Method starts execution" + (dateformat.format(this.startTime)),className,methodName);
    }
    @After("point()")
    public void doAfter(JoinPoint pj) {
        long endTime = System.currentTimeMillis();

        Object target = pj.getTarget();
        String className = target.getClass().getName();  //The class and package to which the currently executed method belongs

        MethodSignature signature = (MethodSignature) pj.getSignature();
        String methodName = signature.getName();  //Name of the currently executed method

       log.info("class{}.{}Method executed" + (endTime - this.startTime) + "ms",className,methodName);
    }
    
    @Around("point()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //Execution method
        Object result = point.proceed();
        //Execution time (MS)
        long time = System.currentTimeMillis() - beginTime;

        //Save log
        saveSysLog(......);

        return result;
    }
}

summary

Call order of filters, interceptors, and slices
Call sequence filter - > interceptor - > aspect - > controller.
The earlier the request is filtered in the system, the smaller the impact on the performance of the service. Therefore, when writing relatively common code, give priority to the filter, then the interceptor, and finally the AOP. For example, permission verification. Generally, all requests require login verification. At this time, the filter should be used to verify at the top level; For example, log recording, generally, logs are only used for some logic, and involve the log records before and after the completion of business logic. Therefore, the use of filters cannot divide modules in detail. At this time, interceptors should be considered. However, interceptors also match rules according to URL s, so they are not detailed enough. Therefore, we will consider using AOP, AOP can intercept the method level of the code, which is very suitable for the logging function.

1. Filter
The Filter can get the original http request, but can't get the information of the requested controller and the method in the request controller. The Filter can intercept the request and response of the method (ServletRequest request, ServletResponse response), and make response like filtering operations on the request response, such as setting character coding, authentication, etc. The Filter depends on the Servlet container and is part of the Servlet specification. The implementation of Filter is completed by the Servlet container callback. The lifecycle of the Filter is managed by the Servlet container.

2. Interceptor
The interceptor can get the controller and method you requested, but can't get the parameters of the requested method. Interceptors exist independently and can be used in any case. Interceptors are usually executed by dynamic proxy (reflection) and managed by IOC container. Therefore, it is more convenient to obtain instances of other beans by injection.

3.Aop slice
AOP can get the parameters of the method, but it can't get the objects of http request and response. AOP operation can intercept the operation horizontally. The biggest advantage is that it can obtain the parameters of the execution method and deal with the method uniformly Common use logs, transactions, request parameters, security verification, etc.

Topics: Java Spring Boot Microservices