Several Ways to Realize Universal Auth Authentication by Springboot

  • 2021-10-27 07:09:09
  • OfStack

Preface to the table of contents
Traditional AOP implementation
Expand
Interceptor Implementation
Expand
ArgumentResolver Implementation Extensions
Filter Extensions
Summary

Preface

Recently, I was overwhelmed by endless business needs and didn't have time to breathe. Finally, I received a job that could make me break through the code comfort zone. The process of solving it was very tortuous. I doubted my life for 1 degree, but I gained a lot and the code was not obvious, but I felt that I had erased a layer of yarn that java, Tomcat and Spring 1 directly blocked in front of my eyes. The understanding of them has reached a new level.

I haven't output it for a long time, so I picked one aspect to summarize it, hoping to know something else in the process of combing. Due to the prosperous ecology of Java, there are a lot of articles devoted to each module below. So I chose another angle, starting from practical problems, and connecting these scattered knowledge in series, so you can look at it as a summary. Each module of the ultimate detailed introduction, you can go to the official documents or look at other blogs on the network.

The requirements are very simple and clear, which is different from the enchanting requirements raised by products: add a general appkey whitelist verification function in our web framework, hoping that its scalability will be better.

This web framework is implemented by the predecessor of the department based on spring-boot, which is between the business and Spring framework, and does some general functions biased towards business, such as log output, function switch, general parameter analysis and so on. Usually transparent to the business, I have been busy doing the requirements well and writing the code well recently, and I have never even noticed its existence.

Traditional AOP

For this requirement, of course, the first thing that comes to mind is the AOP interface provided by Spring-boot, which only needs to add a tangent point before the Controller method, and then process the tangent point.

Realization

Its use steps are as follows:

Use @ Aspect to declare 1 undercut class WhitelistAspect; Add a tangent point whitelistPointcut () in the tangent class. In order to realize the ability of flexible assembly of this tangent point, instead of using execution to intercept all of them, add an annotation @ Whitelist, and the annotated method will verify the white list. Declare 1 notification method checkWhitelist () in the section class using AOP annotation @ Before of spring to verify the whitelist before the Controller method is executed.

The section class pseudo-code is as follows:


 @Aspect
  public class WhitelistAspect {

    @Before(value = "whitelistPointcut() && @annotation(whitelist)")
    public void checkAppkeyWhitelist(JoinPoint joinPoint, Whitelist whitelist) {
        checkWhitelist();
        //  Available  joinPoint.getArgs()  Get Controller Parameters of the method 
        //  You can use the  whitelist  Variable to get annotation parameters 
    }

    @Pointcut("@annotation(com.zhenbianshu.Whitelist)")
    public void whitelistPointCut() {
    }
  }

Add the @ Whitelist annotation to the Controller method to implement functionality.

Expand

In this example, annotations are used to declare tangent points, and I have realized that the white list to be verified is declared through annotation parameters. If other white lists need to be added later, such as UID verification, uid () and other methods can be added for this annotation to realize custom verification.

In addition, AOP of spring also supports tangent declaration methods such as execution (execution method) and bean (execution method matching Bean objects with specific names), and notification methods such as @ Around (execution during target function execution) and @ After (after method execution).

In this way, the function has been realized, but the leader is not satisfied with =_=, because AOP is used too much in the project, and it is used too much. I suggest that I change one way. Well, I had to start. Another concern: Code ape technology column, reply in the background: "Interview Book" can be obtained, and the latest version of HD PDF is 3625 pages of Internet big factory interview questions.

Interceptor

The interceptor of Spring (Interceptor) is also very suitable for this function. As the name implies, the interceptor is used to judge whether to execute this method through one parameter before Action is executed in Controller. To realize one interceptor, the HandlerInterceptor interface of Spring can be realized.

Realization

The implementation steps are as follows:

Define the interceptor class AppkeyInterceptor class and implement the HandlerInterceptor interface. Implement its preHandle () method; In the preHandle method, whether the interception request is needed is judged by annotations and parameters, and the interface returns false when intercepting the request; Register this interceptor in the custom WebMvcConfigurerAdapter class;

The AppkeyInterceptor classes are as follows:


@Component
public class WhitelistInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Whitelist whitelist = ((HandlerMethod) handler).getMethodAnnotation(Whitelist.class);
        // whitelist.values();  Pass  request  Gets the request parameters, using the  whitelist  Variable to get annotation parameters 
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  //  Method in the Controller Execute after the execution of the method ends 
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  //  In view Execute when the view is rendered 
    }
}

Expand

To enable the interceptor, you have to explicitly configure it to be enabled, and here we configure it using WebMvcConfigurerAdapter. Note that the MvcConfiguration that inherits it needs to be in the ComponentScan path.


@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WhitelistInterceptor()).addPathPatterns("/*").order(1);
        //  You can configure the interceptor-enabled  path  When there are multiple interceptors, any 1 Interceptor return  false  Will cause subsequent request methods to no longer execute 
    }
}

It should also be noted that after successful interceptor execution, the response code is 200, but the response data is empty.
After using the interceptor to realize the function, the leader finally made a big move: we already have an Auth parameter, appkey can be taken from Auth parameter, and it can be regarded as a way of Auth to be absent from the white list. Why not check it when Auth? emmm … vomiting blood.

ArgumentResolver

Parameter parser is a tool provided by Spring for parsing custom parameters. Our commonly used @ RequestParam annotation has its shadow. With it, we can combine parameters into what we want before entering Controller Action. Spring maintains one ResolverList, and when the request arrives, Spring finds custom type parameters (non-primitive types), and tries these Resolver in turn until one Resolver can resolve the required parameters. To implement a parameter parser, you need to implement the HandlerMethodArgumentResolver interface.

Realization

Define custom parameter type AuthParam, and there are appkey related fields in the class; Defining AuthParamResolver and implementing HandlerMethodArgumentResolver interface; The method of implementing supportsParameter () interface adapts AuthParam to AuthParamResolver; Implement resolveArgument () interface method to parse reqest object to generate AuthParam object, and check AuthParam here to confirm whether appkey is in the white list; Enable this Resolver by adding an AuthParam parameter to the signature on the Controller Action method;

The following AuthParamResolver classes are implemented:


@Component
public class AuthParamResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(AuthParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Whitelist whitelist = parameter.getMethodAnnotation(Whitelist.class);
        //  Pass  webRequest  And  whitelist  Verify white list 
        return new AuthParam();
    }
}

Expand

Of course, using the parameter parser also needs to be configured separately, and we also configure it within WebMvcConfigurerAdapter:


@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new AuthParamResolver());
    }
}

After this implementation, I still have some unease, so I searched on the Internet whether there are other ways to achieve this function, and found that Filter is common.

Filter

Filter is not provided by Spring, it is defined in the Servlet specification and is supported by the Servlet container. Requests filtered by Filter are not dispatched to Spring containers. Its implementation is also relatively simple, the implementation of javax. servlet. Filter interface can be.

Because it is not in the Spring container, Filter cannot obtain the resources of the Spring container, and can only use ServletRequest and ServletResponse of the native Java to obtain the request parameters.

In addition, the doFilter method calling FilterChain should be displayed in 1 Filter, otherwise the request is considered to be intercepted. Implement something similar:
public class WhitelistFilter implements javax.servlet.Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
  //  Is called after initialization 1 Times 
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     //  Determine whether interception is needed 
       chain.doFilter(request, response); //  Request through the call to display 
    }

    @Override
    public void destroy() {
     //  Called when destroyed 1 Times 
    }
}

Expand

Filter also requires display configuration:


@Configuration
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean someFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new WhitelistFilter());
        registration.addUrlPatterns("/*");
        registration.setName("whitelistFilter");
        registration.setOrder(1); //  Set the order in which filters are called 
        return registration;
    }
}

Summary

Each of the four implementations has its own suitable scenario, so what is the calling order between them?

Filter is implemented by Servlet, which is naturally called first. What is called later is that Interceptor is intercepted and naturally does not need to be processed later, then the parameter parser, and finally the tangent point of the tangent plane. After I implemented all four methods in one project, the output log also proved this conclusion.


Related articles: