“This article has participated in the good writing call, click to see:Back end, big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”

Preface:

Of some very simple function 【 landing function attestation + + interface user information sharing 】 the three functions must be basically mostly encountered in the development of people in daily, if you are still in the use of the interceptor to interface with white list to filter the interface need attestation, if every time you still need to take the user information need to check the db again, So you are worth watching, xiaobian teach you how to play fancy interface login check function 👇👇👇


The body of the

Technical design process

Let’s have a look at the flow chart of implementation, we mainly use techniques include: HandlerMethodArgumentResolver parser (parameters), HandlerInterceptor (vehicle), the thread local variable ThreadLocal; JWT, JWT, JWT, JWT, JWT, JWT, JWT, JWT, JWT

Technical implementation:

The verification function is mainly implemented around two custom annotations:

  • @currentUser: Annotate parameters to obtain user information
  • UserAuthPassPort: Identifies the method that requires validation

Doing so is to use more flexible, the benefits of our code is not large chunks of repetitive user information or some you used to some of the information system, we use ThreadLocal also guarantee the safety of the thread data at the same time, also do not need again when new feature we added white list or delete the white list, etc., And cool enough, XDM!

@UserAuthPassPort
import com.peppa.userserver.auth.starter.annotation.UserAuthPassport; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.METHOD; /** ** @author Taoze * @version 1.0 * @date 7/5/21 10:39am */ @target ({METHOD}) @Retention(retentionpolice.runtime) @UserAuthPassport public @interface UserAuthPassPort { }Copy the code
@CurrentUser
package com.peppa.core.api.common.auth; import java.lang.annotation.*; /** ** Example: @currentUser UserInfo UserInfo * is a Controller parameter, Documented * * @author Taoze * @Version 1.0 * @Date 7/6/21 10:39 AM */ @Documented @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface CoreCurrentUserId { }Copy the code

Ok! Interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor: @UserAuthPassport interceptor

Listener – filter – “interceptors -” HandlerMethodArgumentResolver – “AOP

You can see that AOP is last in the order of execution, especially in the complexity of the system, depending on various other services, there will be a lot of interceptors and so on, so when you use AOP you may not even go to your AOP will be intercepted return;

SsoTokenInterceptor:
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * SsoToken interceptor check ** @author Taoze * @version 1.0 * @date 7/1/21 8:23pm */ @slf4j @Component public class SsoTokenInterceptor implements HandlerInterceptor { @Autowired private UserAuthFeign authFeign; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); if (method.getAnnotation(UserAuthPassPort.class) ! Log.info (" hit interceptor -> Start check "); String userToken = request.getHeader("user-token"); If (stringutils. isBlank(userToken)) {throw new APIException( ); } try { checkoutToken(ssoToken, userToken); } catch (RemoteServerException e) { log.error("authAspectMethod -> checkoutToken is fail ", e); Throw New APIException(" Implement it yourself!" ); }} log.info(" No interceptor hit -> skip checker "); return true; } /** * ssoToken check ** @param ssoToken todo need to add degrade policy * @return */ private void checkoutToken(String ssoToken, String userToken) throws RemoteServerException { UserInfo userInfo = FeignUtil.getResponseData(authFeign.getUserInfoByToken(userToken)); If (null = = userId | | userId < 0) {throw new APIException (" yourself ha!" ); } ThreadContextHolder.setUserInfo(userInfo); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView ModelAndView) throws Exception {log.info(" Request terminated -> Clear ThreadLocal"); ThreadContextHolder.destroy(); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Do nothing because of X and Y. } }Copy the code

To briefly explain the code above, we use the interceptor preHandle to access the Controller method with method.getannotation (UserAuthPassPort. Class)! = null First gets whether the current request method annotates our custom check annotation, if it is standard then goes to the interceptor. CheckoutToken During the checkoutToken check, we need to obtain the user information corresponding to the current userToken through the userToken and save it in the ThreadLocal for future use. Finally, postHandle removes user information from ThreadLocal after the view is generated, or else there may be a memory leak! Remember to clear! Must be cleared! Must be cleared! I’m going to tell you three times why it’s important to clean up and you can look at the implementation of ThreadLocal so I don’t want to talk about it too much;

ThreadContextHolder
/** * Thread ThreadLocal ** @author taoze * @version 1.0 * @date 7/6/21 10:39am */ public class ThreadContextHolder { private ThreadContextHolder() { } private static ThreadLocal<UserInfo> userInfo = new ThreadLocal<>(); public static void setUserInfo(UserInfo us) { userInfo.set(us); } public static UserInfo getUserInfo() { return userInfo.get(); } public static void destroy() { if (userInfo ! = null) { userInfo.remove(); }}}Copy the code

This is a public ThreadLocal method that allows you to make as many changes as you need.

CurrentUserResolver
package com.peppa.core.api.common.auth; import com.peppa.core.api.exception.ServiceException; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** * SpringMvc parameter parsing assignment UserId ** @author taoze * @version 1.0 * @date 7/6/21 3:56pm */ @component public class CoreCurrentUserIdResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(CurrentUser.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { CurrentUser currentUserAnnotation = parameter.getParameterAnnotation(CurrentUser.class); if (currentUserIdAnnotation == null) { throw new ServiceException(-9999, "not found CurrentUser annotation name " + parameter.getParameterName()); } else { UserInfo userInfo = ThreadContextHolder.getUserInfo(); if (userInfo ! = null) { Class<? > parameterClass = parameter.getParameterType(); if (parameterClass.equals(UserInfo.class)) { return userInfo; } } } return null; }}Copy the code

The supportsParameter method will only enter the resolveArgument to verify the existence of the current annotation if it returns true. This method gets the annotation of the input annotation and then the user information in the annotation ThreadLocal. Determine if the annotation annotation parameter is userinfo.class and assign the value to the current parameter.

Ok, let’s test it out:

    @PostMapping("/test")
    @UserAuthPassPort
    public ResponseBody<AuthCodeJson> getAuthCode(@CurrentUser UserInfo userInfo) {
        System.out.println(userInfo.getId());
        System.out.println(ThreadContextHolder.getUserInfo().getUserId);
        return this.success();
    }
Copy the code
Output result:


Ok! This period of the Actual Java combat to this, you have mastered it, I hope you can help, there are wrong places I hope we can put forward, grow together;

Neat makes for great code, and there’s only so much detail