• notice
  • Congratulations on the launch of the Sought Tech site

How SpringBoot implements login interception detailed process through ThreadLocal

1 Introduction

Registration and login can be said to be the most common thing in normal development, but generally after entering the company, functions like this have been developed long ago, unless it is a new project. In the past two days, I happened to encounter such a need to complete the registration and login function on the PC side.

There are many ways to achieve such a requirement: like

1)HandlerInterceptor+WebMvcConfigurer+ThreadLocal

2) Filter filter

3) Security Framework Shiro (Lightweight Framework)

4) Security framework Spring Securety (heavyweight framework)

And I use the first Spring HandlerInterceptor+WebMvcConfigurer+ThreadLocal technology to implement.


2 specific classes


2.1HandlerInterceptor

HandlerInterceptor is an interface provided for interceptors in springMVC, which is similar to the filter in Servlet development. It is used for processor preprocessing and postprocessing, and three methods need to be rewritten.

preHandle:

Call time: before the controller method is processed

Execution order: In the case of chained Intercepters, Intercepters are executed one by one in the order of declaration

If it returns false, the execution will be interrupted. Note: afterCompletion will not be entered

postHandle:

Call premise: preHandle returns true

Call time: After the Controller method is processed, before the DispatcherServlet renders the view, that is to say, the ModelAndView can be operated in this method

Execution order: In the case of chained Interceptors, the Interceptors are executed in the order of declaration

Note: Although postHandle starts with post, post requests and get requests can be processed

afterCompletion:

Call premise: preHandle returns true

Call time: after DispatcherServlet renders the view

Mostly used to clean up resources


2.2WebMvcConfigurer

The WebMvcConfigurer configuration class is actually an Springinternal configuration method. It adopts JavaBeanthe form to replace the traditional xmlconfiguration file form to customize the framework. Some Handler, Interceptor, ViewResolver, and MessageConverter can be customized. Based on the java-based spring mvc configuration, you need to create a configuration class and implement the WebMvcConfigurerinterface;

In Spring Boot version 1.5, custom interceptors, message converters, etc. are added by overriding the method of WebMvcConfigurerAdapter. After SpringBoot 2.0, this class is marked as @Deprecated. The official recommendation is to directly implement WebMvcConfigurer or directly inherit WebMvcConfigurationSupport. The first method implements the WebMvcConfigurer interface (recommended), and the second method inherits the WebMvcConfigurationSupport class.


3 Code Practice

1) Write the interceptor HeadTokenInterceptor to inherit HandlerInterceptor

package com.liubujun.config;
import com.liubujun.moudle.UserToken;
import com.liubujun.util.SecurityContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.handler.Handler;
import java.io.IOException;
/**
* @Author: liubujun
* @Date: 2022/5/21 16:12
*/
@Component@Slf4j public class HeadTokenInterceptor implements HandlerInterceptor {
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       String authorization = request.getHeader("Authorization");
       if (authorization == null ) {
           unauthorized(response) ;
           return false;
       }
       //The value of userToken is generally parsed here, and UserToken is directly new here for convenience
       userToken = new UserToken();
       SecurityContextUtil.addUser(userToken);
       return false;
   }
   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
   }
   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
       SecurityContextUtil.removeUser();
   }
   private void unauthorized(HttpServletResponse response) {
       response.setStatus(HttpStatus.UNAUTHORIZED.value());
       try {
           response.getWriter().append(HttpStatus.UNAUTHORIZED.getReasonPhrase());
       } catch (IOException e) {
           log.error("HttpServletResponse writer error.msg",HttpStatus.UNAUTHORIZED.getReasonPhrase());
           log.error(e.getMessage(),e);
       }
   }}

2) Write MyWebMvcConfigurer to inherit WebMvcConfigurationSupport

package com.liubujun.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.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.ArrayList;/**
* @Author: liubujun
* @Date: 2022/5/21 16:40
*/@Configurationpublic class MyWebMvcConfigurer extends WebMvcConfigurationSupport {
   @Autowired
   private HeadTokenInterceptor headTokenInterceptor;
   /**
    * Similar to the whitelist, requests added here will not go through the interceptor
    * @param registry
    */
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       ArrayList<String> pattres = new ArrayList<> ();
       pattres.add("/login/login");
       registry.addInterceptor(headTokenInterceptor).excludePathPatterns(pattres).addPathPatterns("/**");
       super.addInterceptors(registry);
   }
   /**
    * add static Resource
    * @param registry
    */
   @Override
       registry.addResourceHandler("xxx.html")
               .addResourceLocations("classpath:/META-INF/resources");
       super.addResourceHandlers(registry);
   }}

3) Write a ThreadLocal class to store user information

package com.liubujun.util;import com.liubujun.moudle.UserToken;import org.springframework.core.NamedThreadLocal;/**
* @Author: liubujun
* @Date: 2022/5/23 9:41
*/public class SecurityContextUtil {
   private static ThreadLocal<UserToken> threadLocal = new NamedThreadLocal<>("user");
   public static void addUser(UserToken user){
       threadLocal.set(user);
   }
   public static UserToken getUser(){
       return threadLocal.get();
   }
   public static void removeUser(){
       threadLocal.remove();
   }
   public static String getPhoneNumber(){
       return threadLocal.get().getPhoneNumber();
   }
   public static Integer getId(){
       return threadLocal.get().getId();
   }
   public static String getUserText(){
       return threadLocal.get().getUserText();
   }}

4) Write a test controller

@RestController @RequestMapping(value = "/login",produces = {"application/json;charset=UTF-8"}) public class Login { 
   @PostMapping("/login")
   public String login(){
       return "Login request No need to intercept";
   }
   @PostMapping("/other")
   public String other(){
       return "Other requests need to be intercepted";
   } }

5) Test

Test the login interface, (let it go directly without passing the token)

Test other interfaces without passing the token to be intercepted


Tags

Technical otaku

Sought technology together

Related Topic

0 Comments

Leave a Reply

+