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 Spring
internal configuration method. It adopts JavaBean
the form to replace the traditional xml
configuration 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 WebMvcConfigurer
interface;
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
0 Comments