Maison  >  Article  >  Java  >  Comment SpringBoot implémente l'interception de connexion via ThreadLocal

Comment SpringBoot implémente l'interception de connexion via ThreadLocal

WBOY
WBOYavant
2023-05-22 12:04:421310parcourir

    1 Préface

    L'inscription et la connexion peuvent être considérées comme les choses les plus courantes dans le développement quotidien, mais généralement après l'entrée dans l'entreprise, des fonctions comme celle-ci ont été développées il y a longtemps, à moins qu'il ne s'agisse d'un nouveau projet. Au cours des deux derniers jours, j'ai rencontré un tel besoin de compléter la fonction d'enregistrement et de connexion côté PC.

    Il existe de nombreuses façons de répondre à de tels besoins : comme

    1) HandlerInterceptor+WebMvcConfigurer+ThreadLocal

    2) Filtre de filtre

    3) Cadre de sécurité Shiro (cadre léger)

    4) Cadre de sécurité Spring Securety (cadre lourd)

    Et j'utilise la première technologie Spring HandlerInterceptor+WebMvcConfigurer+ThreadLocal pour l'implémenter.

    2 Classe concrète

    2.1HandlerInterceptor

    HandlerInterceptor est l'interface fournie pour les intercepteurs dans springMVC. Elle est similaire au filtre dans le développement de Servlet. Elle est utilisée pour le prétraitement et le post-traitement par le processeur.

    preHandle :

    Temps d'appel : avant le traitement de la méthode du contrôleur

    Ordre d'exécution : dans le cas d'un Intercepteur chaîné, les Intercepteurs sont exécutés les uns après les autres dans l'ordre de déclaration

    Si false est renvoyé, l'exécution sera interrompue. . Remarque : il n'entrera pas aprèsCompletion

    postHandle :

    Prémisse d'appel : preHandle renvoie true

    Heure d'appel : après le traitement de la méthode Controller et avant que DispatcherServlet ne restitue la vue, c'est-à-dire que ModelAndView peut être utilisé dans cette méthode.

    Ordre d'exécution : dans le cas d'un intercepteur chaîné, l'intercepteur est exécuté dans l'ordre de déclaration

    Remarques : bien que postHandle commence par post, les demandes de publication et les demandes d'obtention peuvent être traitées

    afterCompletion :

    Prémisse d'appel : preHandle renvoie true

    Temps d'appel : après que DispatcherServlet ait rendu la vue

    Utilisations multiples Pour nettoyer les ressources

    2.2 WebMvcConfigurer

    La classe de configuration WebMvcConfigurer est en fait une Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurerinterface

    Dans la version Spring Boot 1.5, nous nous appuyons sur la méthode de réécriture de WebMvcConfigurerAdapter ; pour ajouter des intercepteurs personnalisés, des convertisseurs de messages, etc. Après la version SpringBoot 2.0, cette classe a été marquée @Deprecated (obsolète). La recommandation officielle est d'implémenter directement WebMvcConfigurer ou d'hériter directement de WebMvcConfigurationSupport. La première méthode consiste à implémenter l'interface WebMvcConfigurer (recommandée), et la deuxième méthode consiste à hériter de la classe WebMvcConfigurationSupport

    3 Pratique du code

    1) Écrivez l'intercepteur HeadTokenInterceptor dans hériter du 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;
            }
            //这里一般都会解析出userToken的值,这里为了方便就直接new了
            UserToken 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) Écrivez MyWebMvcConfigurer pour le faire hériter de 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
     */
    @Configuration
    public class MyWebMvcConfigurer extends WebMvcConfigurationSupport {
        @Autowired
        private HeadTokenInterceptor headTokenInterceptor;
        /**
         * 类似于白名单,在这边添加的请求不会走拦截器
         * @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);
        }
        /**
         * 添加静态资源
         * @param registry
         */
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("xxx.html")
                    .addResourceLocations("classpath:/META-INF/resources");
            super.addResourceHandlers(registry);
        }
    }

    3) Écrivez la classe ThreadLocal pour stocker les informations utilisateur

    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) Écrivez un contrôleur de test

    @RestController
    @RequestMapping(value = "/login",produces = {"application/json;charset=UTF-8"})
    public class Login {
        @PostMapping("/login")
        public String login(){
            return "登录请求不需要拦截";
        }
        @PostMapping("/other")
        public String other(){
            return "其他的请求需要拦截";
        }
    }

    5) Test

    Testez l'interface de connexion, (libérez-le directement sans passer le token)

    Comment SpringBoot implémente linterception de connexion via ThreadLocal

    Testez d'autres interfaces, non Le token a été intercepté

    Comment SpringBoot implémente linterception de connexion via ThreadLocal

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Déclaration:
    Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer