SpringBoot常用拦截器(HandlerInterceptor,ClientHttpRequestInterceptor,RequestInterceptor)

2023-05-16

一、SpringBoot常用拦截器

下面3种拦截器,都是http拦截器,在处理业务逻辑之前对http请求信息进行处理,比如获取请求头,请求参数,设置请求头,请求参数等等

思路清晰,先说jar包:

  • HandlerInterceptor—>spring-webmvc项目,org.springframework.web.servlet.HandlerInterceptor
  • ClientHttpRequestInterceptor—>spring-web项目,org.springframework.http.client.ClientHttpRequestInterceptor
  • RequestInterceptor—>feign-core项目,feign.RequestInterceptor

一目了然,从项目名称和包路径可以看出,3个拦截器分别属于3个不同的项目,所以他们之前的作用也有区别,在这里我大概讲一下3个拦截器的基本应用和区别:

3个拦截器的共同点,都是对http请求进行拦截,但是http请求的来源不同:

  • HandlerInterceptor是最常规的,其拦截的http请求是来自于客户端浏览器之类的,是最常见的http请求拦截器;
  • ClientHttpRequestInterceptor是对RestTemplate的请求进行拦截的,在项目中直接使用restTemplate.getForObject的时候,会对这种请求进行拦截,经常被称为:RestTempalte拦截器或者Ribbon拦截器;
  • RequestInterceptor常被称为是Feign拦截器,由于Feign调用底层实际上还是http调用,因此也是一个http拦截器,在项目中使用Feign调用的时候,可以使用此拦截器;

二、使用及说明

2.1 HandlerInterceptor

从包路径可以看出,这个是处理客户端http servlet请求的,此项目spring-webmvc与spring-mvc项目关闭密切,HandlerInterceptor可以对请求的各个阶段进行拦截,可以说是非常全面了。这个也是常规项目中用的最多的,对http请求进行拦截

public interface HandlerInterceptor {


    /**前置处理:在业务处理器处理请求之前被调用*/
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception;

	/**中置处理:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView ,现在这个很少使用了*/
	void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception;

    /**后置处理:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等*/
	void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception;

注意:只有perHandle方法返回true的时候,postHandle 和 afterCompletion 方法才会执行,如果业务不需要,就不用重写postHandle 和 afterCompletion 方法了

preHandle

调用时间:Controller方法处理之前

执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行

若返回false,则中断执行,注意:不会进入afterCompletion

postHandle

调用前提:preHandle返回true

调用时间:Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作

执行顺序:链式Intercepter情况下,Intercepter按照声明的顺序倒着执行。

备注:postHandle虽然post打头,但post、get方法都能处理

afterCompletion

调用前提:preHandle返回true

调用时间:DispatcherServlet进行视图的渲染之后

多用于清理资源

因为HandlerInterceptorAdapter实现了HandlerInterceptor接口,下面是使用示例


public class JWTInterceptor implements HandlerInterceptor {


    // 重写preHandle方法,在请求发生前执行,此处对每个请求的token进行校验
    // 但是登陆的时候没有token就不能对登陆的接口进行拦截,所以要设置自定义的拦截规则

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        String token = request.getHeader("token");  //从request中获取到请求头中的token,进行解析校验
        try {
            jwtUtil.verifyToken(token);//调用token解析的工具类进行解析
            return true;  //请求放行
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg", "签名不一致异常");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg", "令牌过期异常");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg", "算法不匹配异常");
        } catch (InvalidClaimException e) {
            e.printStackTrace();
            map.put("msg", "失效的payload异常");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg", "token无效");
        }
        //map异常的数据要返回给客户端需要转换成json格式  @ResponseBody 内置了jackson
        String resultJson = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().print(resultJson);
        return false;  //异常不放行
    }

    // 当preHandle方法返回值为true的时候才会执行。
    // 重写postHandle方法,在请求完成后执行。
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle执行了");
    }

    // 当preHandle方法返回值为true的时候才会执行。
    // 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion执行了");
    }
}


上边定义的将会在每个方法执行的时候都进行拦截,我们可以自定义拦截规格,拦截那些路径,不拦截哪些路径,需要自定义拦截规则:

//配置拦截条件
@Configuration
public class interceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //参数为我们自定义类,实现了HandlerInterceptor接口重写了三个方法
        
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/interceptorVerify/**")   //拦截所有的路径
                .excludePathPatterns("/login/**"); //放行login目录下的,因为生成token拦截就无法生成了
    }
}

参考:https://blog.csdn.net/weixin_46649054/article/details/118355986

2.2 ClientHttpRequestInterceptor

public interface ClientHttpRequestInterceptor {

	/**只有这一个方法,在项目中直接使用 restTemplate.getForObject 的时候,会对这种请求进行拦截*/
   ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
         throws IOException;

使用示例(SESSIONID可以从RequestContextHolder中拿到)

public class RestClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        HttpHeaders headers = request.getHeaders();
        headers.add("Cookie","SESSIONID=b8dd5bd9-9fb7-48cb-a86b-e079cb554fb8");
        log.info("拦截器已添加header");
        return execution.execute(request,body);
    }
}

配置config类(此处注入bean时需要给方法起个名称,注意不要使用restTemplate,否则使用时不会进入该方法中)

@Slf4j
@Configuration
public class RestTemplateCrsConfig {

    @Bean(name = "restTemplateToken")
    public RestTemplate restTemplate(HeaderRequestInterceptor  headerRequestInterceptor) {
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        interceptors.add(headerRequestInterceptor);
        RestTemplate restTemplate = new RestTemplate();
        //统一加token
        restTemplate.setInterceptors(interceptors);

        return restTemplate;
    }
}

引用注入的RestTemplate(此处注意,引用时名称和注入式名称一致,例如注入为restTemplateToken,则类名也为restTemplateToken,如下代码),此时调用时就会将自定义的token值放入header中一并传给接收方,接收方根据token进行鉴权。

	@Autowired
    private RestTemplate restTemplateToken;```
 public void updater(String projectId, String ame) {
     String url = 127.0.0.1:8080/test;
        try {
            restTemplateCrs.setErrorHandler(new RestTemplateErrorHandler());
            ResponseEntity<ResultModel> response = restTemplateCrs.postForEntity(url, name, ResultModel.class);
            ResultModel body = response.getBody();

2.3 RequestInterceptor

public interface RequestInterceptor {

  /**在项目中使用Feign调用的时候,可以使用此拦截器*/
  void apply(RequestTemplate template);
}

使用示例,实现登录用户信息在微服务之间的传递
参考:https://blog.csdn.net/liuerchong/article/details/123765305

实现思路:

1:准备一个ThreadLocal变量,供线程之间共享。

2:每个微服务对所有过来的Feign调用进行过滤,然后从请求头中获取User用户信息,并存在ThreadLocal变量中。

3:每个微服务在使用FeignClient调用别的微服务时,先从ThreadLocal里面取出user信息,并放在request的请求头中。

4:封装为一个注解,在启动类上标记即可。

1:ThreadLocal工具类 :UserInfoContext

public class UserInfoContext {
    private static ThreadLocal<UserInfo> userInfo = new ThreadLocal<UserInfo>();
    public static String KEY_USERINFO_IN_HTTP_HEADER = "X-AUTO-FP-USERINFO";

    public UserInfoContext() {
    }

    public static UserInfo getUser(){
        return (UserInfo)userInfo.get();
    }

    public static void setUser(UserInfo user){
        userInfo.set(user);
    }
}

2:准备承载用户信息的userInfo实体类
编写拦截器 : TransmitUserInfoFeighClientIntercepter

public class TransmitUserInfoFeighClientIntercepter implements RequestInterceptor {

    private static final Logger log = LoggerFactory.getLogger(TransmitUserInfoFeighClientIntercepter.class);
    public TransmitUserInfoFeighClientIntercepter() {
    }

    @Override
    public void apply(RequestTemplate requestTemplate) {
        //从应用上下文中取出user信息,放入Feign的请求头中
        UserInfo user = UserInfoContext.getUser();
        if (user != null) {
            try {
                String userJson = JSON.toJSONString(user);
                requestTemplate.header("KEY_USERINFO_IN_HTTP_HEADER",new String[]{URLDecoder.decode(userJson,"UTF-8")});
            } catch (UnsupportedEncodingException e) {
                log.error("用户信息设置错误",e);
            }
        }
    }
}

4:编写过滤器:TransmitUserInfoFilter

public class TransmitUserInfoFilter implements Filter {

    private static final Logger log = LoggerFactory.getLogger(TransmitUserInfoFeighClientIntercepter.class);
    public TransmitUserInfoFilter() {
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       this.initUserInfo((HttpServletRequest)request);
       chain.doFilter(request,response);
    }

    private void initUserInfo(HttpServletRequest request){
        String userJson = request.getHeader("KEY_USERINFO_IN_HTTP_HEADER");
        if (StringUtils.isNotBlank(userJson)) {
            try {
                userJson = URLDecoder.decode(userJson,"UTF-8");
                UserInfo userInfo = (UserInfo) JSON.parseObject(userJson,UserInfo.class);
                //将UserInfo放入上下文中
                UserInfoContext.setUser(userInfo);
            } catch (UnsupportedEncodingException e) {
               log.error("init userInfo error",e);
            }
        }
    }

    @Override
    public void destroy() {
    }
}

5:编写注解实现类: EnableUserInfoTransmitterAutoConfiguration

@Configuration
public class EnableUserInfoTransmitterAutoConfiguration {

    public EnableUserInfoTransmitterAutoConfiguration() {
    }

    @Bean
    public TransmitUserInfoFeighClientIntercepter transmitUserInfo2FeighHttpHeader(){
       return new TransmitUserInfoFeighClientIntercepter();
    }

    @Bean
    public TransmitUserInfoFilter transmitUserInfoFromHttpHeader(){
        return new TransmitUserInfoFilter();
    }
}

编写注解 EnableUserInfoTransmitter

@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({EnableUserInfoTransmitterAutoConfiguration.class})
public @interface EnableUserInfoTransmitter {
}

在启动类上标记注解即可使用


@SpringBootApplication

@EnableUserInfoTransmitter
public class TestCommonClient {

    public static void main(String[] args){

        SpringApplication.run(TestCommonClient.class,args);
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot常用拦截器(HandlerInterceptor,ClientHttpRequestInterceptor,RequestInterceptor) 的相关文章

随机推荐

  • 电脑设置ftp共享文件的方法

    1 首先打开控制面板 xff0c 找到程序 打开或关闭Windows功能选项 2 找到internet信息服务项把其下面的所有子功能全部勾选 3 等待短时间服务配置完成后 xff0c 右键我的电脑打开管理 xff0c 选择internet信
  • C#窗体控件--button

    本文介绍C 窗体控件的 button 该控件是窗体中实现按钮点动 xff0c 触发事件则由程序逻辑进行 操作流程 1 1 添加控件 新建一个窗体 xff0c 在界面中添加Button控件如下所示 xff1a 1 2 控件属性设置 设置控件的
  • Android APK资源加载流程

    概述 我们在Activity中访问资源 xff08 图片 xff0c 字符串 xff0c 颜色等 xff09 是非常方便的 xff0c 只需要getResources 获取一个Resources对象 xff0c 然后就可以访问各种资源了 x
  • 倍福位置记忆--TwinCAT对绝对值编码器溢出圈数的处理--以汇川IS620N为例

    首先配置伺服 xff0c 如下所示 xff1a 根据伺服手册和编码器反馈的数值可知 xff0c 其每转脉冲数 xff0c 和最大的记忆圈数 xff1a 型号 xff1a IS620N 编码器位数 xff1a 8388608 最大 xff1a
  • 【LeetCode 】160. 相交链表(高频!字节面试题 双指针法 Python 7行代码 中学追及问题)

    1 题目描述 编写一个程序 xff0c 找到两个单链表相交的起始节点 如下面的两个链表 xff1a 在节点 c1 开始相交 示例 1 xff1a 输入 xff1a intersectVal span class token operator
  • Android中Paint字体的灵活使用

    在Android开发中 xff0c Paint是一个非常重要的绘图工具 xff0c 可以用于在控制台应用程序或Java GUI应用程序中绘制各种形状和图案 其中 xff0c Paint setText 方法是用于设置Paint绘制的文本内容
  • HTML5字体集合的实践经验

    随着互联网的发展 xff0c 网站已成为人们获取信息和交流的重要平台 而一个好的网站 xff0c 不仅需要有美观的界面 xff0c 还需要有良好的用户体验 其中 xff0c 字体是影响用户体验的一个重要因素 下面就让我们来看看HTML字体集
  • 20220806 美团笔试五道编程题(附AK题解)

    恭喜发现宝藏 xff01 微信搜索公众号 TechGuide 回复公司名 xff0c 解锁更多新鲜好文和互联网大厂的笔经面经 作者 64 TechGuide 全网同名 点赞再看 xff0c 养成习惯 xff0c 您动动手指对原创作者意义非凡
  • css中样式类型及属性值的获取

    前言 以前真的没怎么重视 xff0c 然后突然就遇到了与之相关的一个问题 xff0c 最后百度解决了这个问题 xff0c 因此简单记录一下 css样式类型 css样式主要分为三种类型 xff1a 1 内联样式 xff08 行内样式 xff0
  • Ubuntu 18.04版本设置root账户

    Linux系统下文件的权限十分重要 xff0c 大多数操作都需要一定的权限才可以操作 xff0c Ubuntu18 04默认安装是没有设置root账户的 xff0c 因此想要获得root账户登录可以使用以下步骤 xff1a 1 首先获得临时
  • Content-Type: application/vnd.ms-excel 操作文件

    如果要将查询结果导出到Excel xff0c 只需将页面的Context Type修改一下就可以了 xff1a header Content Type application vnd ms excel gt 如果希望能够提供那个打开 保存的
  • win7重装的坑:启动分区不存在 使用分区工具修正

    其实安装win7几个步骤 xff1a 制作启动硬盘 xff08 先制作启动盘 xff0c 再将下载好的ios镜像文件放入 xff09 使用一键安装工具安装系统修改引导启动项 原来的系统盘上面会有原来的主引导文件 xff08 MBR MSR格
  • IN和EXISTS的区别和使用

    一 结论 in 适合子表比主表数据小的情况 exists 适合子表比主表数据大的情况 当主表数据与子表数据一样大时 in与exists效率差不多 可任选一个使用 二 区别 2 1 in的性能分析 select from A where id
  • Android自定义ViewGroup交互进阶,右滑进入详情

    自定义Viewgroup右滑进入详情 前言 在之前的 ViewGroup 的事件相关一文中 xff0c 我们详细的讲解了一些常见的 ViewGroup 需要处理的事件与运动的方式 我们了解了如何处理拦截事件 xff0c 如何滚动 xff0c
  • spring事务实现的几种方式

    一 前言 1 事务几种实现方式 xff08 1 xff09 编程式事务管理对基于 POJO 的应用来说是唯一选择 我们需要在代码中调用beginTransaction commit rollback 等事务管理相关的方法 xff0c 这就是
  • Java实现通过正则表达式判断一个字符串是否含有指定字符

    场景 1 校验一个字符串是否包含有指定字符 这个很简单 xff0c 就单纯的用indexOf或者Contains方法实现就可以了 2 校验一个字符串是否包含的特殊字符 特殊字符虽有多个但也只有几个 xff0c 双重for循环套用indexO
  • springboot使用@SpringBootTest注解进行单元测试

    一 示例 1 1 添加依赖 span class token prolog lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt span span class token tag s
  • 比较两个List是否相等

    1 直接使用equals 比较 众所周知 xff0c 两个列表具有完全相同的元素并且具有完全相同的顺序时 xff0c 它们是相等的 因此 xff0c 如果我们业务要求两个list顺序一致 xff0c 可以使用equals xff08 xff
  • JVM-StringTable(三)

    一 常量池与串池StringTable的关系 span class token keyword public span span class token keyword class span span class token class n
  • SpringBoot常用拦截器(HandlerInterceptor,ClientHttpRequestInterceptor,RequestInterceptor)

    一 SpringBoot常用拦截器 下面3种拦截器 xff0c 都是http拦截器 xff0c 在处理业务逻辑之前对http请求信息进行处理 xff0c 比如获取请求头 xff0c 请求参数 xff0c 设置请求头 xff0c 请求参数等等