SpringBoot拦截器获取Request的body数据

1. 场景

自定义Token后,需要在拦截器中进行token验证。在验证的过程中需要读取HttpServletRequest的body部分数据进行验证。

2. 存在问题

  如果直接配置拦截器进行urlPatterns拦截,并进行参数验证,在拦截器中获取request的输入流,会导致controller层因为无法获取到request相应参数而抛出异常,从而拦截器再次拦截/error请求。
在这里插入图片描述

3.解决办法

  在拦截器执行之前,进行过滤器配置,在过滤器中对Request进行包装,HttpServletRequestWrapper 是 httpServletRequest 的包装类可以很好的解决这个问题。

3.1 配置拦截器,实现HandlerInterceptor

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class BaseInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String token = request.getHeader("token") != null
                ? (String) request.getHeader("token") : "";
        if(StringUtils.isNullOrEmpty(token)){
            return false;
        }

        log.info("[preHandle] executing... request uri is {}", request.getRequestURI());
        if(isJson(request)){
        	String jsonParam = ((BodyReaderHttpServletRequestWrapper)request).getBodyStr();
            //业务逻辑
        }else{
            log.info("参数不为JSOn格式");
            return false;
        }
    }

    private boolean isJson(HttpServletRequest request) {
        if (request.getContentType() != null) {
            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||
                    request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);
        }

        return false;
    }
}

3.2 注册拦截器

addPathPatterns(“/**”)代表拦截全部层级uri,patterns是不拦截的请求uri。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.*;

@Configuration
public class InterceptorConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        BaseInterceptor baseInterceptor = new BaseInterceptor();

        List<String> patterns = new ArrayList<>();
        patterns.add("/helloworld");
        registry.addInterceptor(baseInterceptor).addPathPatterns("/**").excludePathPatterns(patterns);
    }
}

3.2 配置HttpServletRequestWrapper

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.*;
import java.nio.charset.Charset;

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {


    private final byte[] body;
    private String bodyStr;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String bodyString = getBodyString(request);
        body = bodyString.getBytes(Charset.forName("UTF-8"));
        bodyStr=bodyString;
    }

    public String getBodyStr() {
        return bodyStr;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }


    public  String getBodyString(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(
                    new InputStreamReader(inputStream, Charset.forName("UTF-8")));

            char[] bodyCharBuffer = new char[1024];
            int len = 0;
            while ((len = reader.read(bodyCharBuffer)) != -1) {
                sb.append(new String(bodyCharBuffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

3.3 配置过滤器

注意:这里只是将POST请求的request进行包装,具体实现根据业务需求进行更改。

import org.springframework.context.annotation.ComponentScan;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
@ComponentScan
@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"})
public class HttpServletRequestWrapperFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("doFilter");
        ServletRequest requestWrapper = null;

        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            //遇到post方法才对request进行包装
            String methodType = httpRequest.getMethod();
            if ("POST".equals(methodType)) {
                requestWrapper = new BodyReaderHttpServletRequestWrapper(
                        (HttpServletRequest) request);
            }
        }
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }


    @Override
    public void destroy() {

    }
}

4 补充

  1. 配置的过滤器一定要能被SpringBootApplication,即启动类扫描到,可以在启动类上加@ServletComponentScan,为了规范也可以在*Filter类上加@ComponentScan注解。
  2. 解释为什么拦截器中读取了body数据,controller不能再次读取

我们在读取body数据中,是从流中进行读取,全部或部分数据存储在内存中。
以InputStream为例,在读取过程中,会记录当前读取到的位置(mark position),第二次读取是从标志位置继续读取,当然也可以使用reset()方法重置(mark position)。
但是ServletInputStream 中不能调用reset方法,也就是你第一次读取如果将body数据全部加载,第二次读取的数据将会为空。

  1. AOP方法
    拦截器中读取body数据也可以使用aop方法进行读取,具体参考以下文章。
    aop读取body数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/21080.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网易云商·七鱼智能客服自适应 ProtoStuff 数据库缓存实践

需求背景 目前&#xff0c;网易云商七鱼智能客服数据库缓存使用了 spring-data-redis 框架&#xff0c;并由自研的缓存组件进行管理。该组件使用 Jackson 框架对缓存数据进行序列化和反序列化&#xff0c;并将其以明文 JSON 的形式存储在 Redis 中。 这种方式存在两个问题&…

Servlet技术及代码实现

Servlet概念 Servlet是JavaEE规范之一&#xff0c;规范指的就是接口。Servlet是JavaWeb三大组件之一&#xff0c;三大组件分别是:Servlet程序、Filter过滤器、Listener监听器。Servlet是运行在服务器上的一个java小程序&#xff0c;它可以接收客户端发送过来的请求&#xff0c…

Redis高可用--持久化

在Web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准实在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提提供正常服务&a…

绝地求生 压枪python版

仅做学习交流&#xff0c;非盈利&#xff0c;侵联删&#xff08;狗头保命) 一、概述 1.1 效果 总的来说&#xff0c;这种方式是通过图像识别来完成的&#xff0c;不侵入游戏&#xff0c;不读取内存&#xff0c;安全不被检测。 1.2 前置知识 游戏中有各种不同的枪械&#x…

今天面了个京东拿28K 出来的,让我见识到了测试界的天花板

今年的春招基本已经结束了&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的软件测试面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#…

【C生万物】 指针和数组笔试题汇总 (下)

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《C生万物 | 先来学C》&#x1f448; 前言&#xff1a; 承接上文&#xff0c;继续进行指针和数组的练习。 目录 Part2:指针笔试题 1.做题 …

无人值守的IDC机房动环综合运维方案

企业数字化转型以及5G、物联网、云计算、人工智能等新业态带动了数据中心的发展&#xff0c;在国家一体化大数据中心及“东数西算”节点布局的推动下&#xff0c;数据中心机房已成为各大企事业单位维持业务正常运营的重要组成部分&#xff0c;网络设备、系统、业务应用数量与日…

远程桌面连接不上解决方法

远程桌面连接是一种方便快捷的技术&#xff0c;可以让用户在不同的设备之间共享桌面和访问远程计算机。然而&#xff0c;有时候我们可能会遇到远程桌面连接无法正常连接的问题。在本篇文章中&#xff0c;我们将详细介绍远程桌面连接无法连接的常见原因&#xff0c;并提供相对应…

chatgpt赋能Python-python3m

Python 3: 一个新时代的编程语言 在编程的世界里&#xff0c;Python 3可能是当前最为热门的编程语言。Python 3是Python语言的最新版本&#xff0c;代码更加现代化&#xff0c;更加简洁易懂&#xff0c;比Python 2更加易于使用。这篇文章将介绍Python 3的各种特性&#xff0c;…

搭建Serv-U FTP服务器共享文件外网远程访问「无公网IP」

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 转载自内网穿透工具的文章&#xff1a;使用Serv-U搭建FTP服务器并公网访问【内网穿透】 1. 前言…

云贝餐饮连锁V2-2.7.7 【新增】外卖新订单提醒

独立版&#xff1a;云贝餐饮连锁V2、版本更新至2.7.7&#xff0c;小程序、公众号版本&#xff0c;全插件&#xff0c;包含微信公众号小程序&#xff1b;包更新&#xff0c;独立版&#xff1b; 带商家端&#xff0c;修复收银台、排队点餐、堂食点餐&#xff1b;最新版更新了&…

openwrt修改web网页默认端口

使用SSH登录openwrt后台&#xff1b; openwrt 中默认使用的web服务器是uhttpd&#xff0c; 进入配置文件路径&#xff1a; cd /etc/config/ 使用vim编辑器修改uhttpd文件 vi uhttpd 修改以上标红部分后面的端口 vim编辑器打开uhttpd 文件 修改完成后点键盘ESC退出编辑状态&a…

一个胖乎乎的3D卡片(有点像捏扁的圆柱体)

先上效果图&#xff08;图片是随机的&#xff0c;可能你们看到的和这个不一样。但效果是相同的&#xff09;&#xff1a; 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Titl…

LitCTF2023 wp re最后一道 cry misc

本来不打算放了&#xff0c;但是比赛打都打了留个纪念社工有佬&#xff0c;与我无关&#xff0c;misc只会隐写虽然我是逆向手&#xff0c;但因为队友tql&#xff0c;所以只留给我最后一道~~我的wp向来以简述思路为主&#xff0c;习惯就好 Crypto Hex&#xff1f;Hex&#xff…

【扰动识别】S变换电能质量扰动识别(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

一年换 3 份工作,在家躺平半年了!

见字如面&#xff0c;我是军哥&#xff01; 还记得&#xff0c;我之前写的这篇《三年换 3 份工作&#xff0c;再也找不到工作了&#xff01;》文章&#xff1f;上周有一位读者在这篇文章下留言&#xff0c;说自己去年一年换了 3 份工作&#xff0c;目前在家躺了半年了&#xff…

[Android Studio Tool]在Android Studio项目中如何使用CSV文件

文章目录 在Android Studio项目中如何使用CSV文件1. 前情提要&#xff1a;CSV文件的准备2. CSV文件在安卓项目一般存放在什么位置&#xff1f;3. Android Studio处理CSV文件的常用插件&#xff1f;4. 怎么调整Android Studio中对GBK编码的CSV文件的处理&#xff1f;以下是一个代…

Nexus私服搭建与使用

文章目录 1 私服简介2 私服安装步骤1:下载解压步骤2:启动Nexus步骤3:浏览器访问步骤4:首次登录重置密码 3 私服仓库分类4 本地仓库访问私服配置步骤1:私服上配置仓库步骤2:配置本地Maven对私服的访问权限步骤3:配置私服的访问路径 5 私服资源上传与下载步骤1:配置工程上传私服的…

现在的00后,真是卷死了呀,辞职信已经写好了·····

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;三月份春招我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪23K&#xff0c;都快接近我了。 后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了…

借助TeeChart 图表控件,这家公司轻松创建了可视化图表看板

TeeChart for .NET是优秀的工业4.0 WinForm图表控件&#xff0c;官方独家授权汉化&#xff0c;集功能全面、性能稳定、价格实惠等优势于一体。TeeChart for .NET 中文版还可让您在使用和学习上没有任何语言障碍&#xff0c;至少可以节省30%的开发时间。技术交流Qqun&#xff1a…