SpringBoot拦截器获取Request的body数据

news2024/11/28 0:44:30

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数据

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能防盗防偷门锁语音方案设计

智能锁主要功能 防撬报警功能&#xff08;非必须&#xff0c;但很实用&#xff09;&#xff1a;防撬报警功能可以说是指纹密码锁功能中对提升家居安全有效的功能之一。当指纹锁受到外暴力破坏时&#xff0c;就会自动发出警报声&#xff0c;提醒小区安保。好一点的甚至可以自动…

【AUTOSAR】【以太网】UdpNM

目录 一、概述 二、限制与约束 三、功能说明 3.1 协调算法 3.2 操作模式 3.2.1 Network Mode 3.2.2 准备总线睡眠模式 3.2.3 准备总线睡眠模式 3.3 网络状态 3.4 初始化 3.5 通信调度 3.5.1 NM消息发送 3.5.2 NM消息接收 3.6 其他功能 3.7 帧结构 四、API接口 …

创新案例 | 肆拾玖坊白酒0到20亿增长是传销还是创新

01.背景介绍 中国证券报引用公开数据显示&#xff0c;2016年&#xff0c;规模以上白酒企业数量为1578家&#xff0c;2021年&#xff0c;这一数字下降到965家。 同时&#xff0c;白酒产能逐年向优势产区集中&#xff0c;头部企业市场占有率不断提高。2021年&#xff0c;茅台、…

性能测试-操作和优化分析

打流工具 iperf 测试吞吐率 服务端&#xff1a;iperf -u -s 客户端&#xff1a;iperf -u -c 1.1.1.1 -b 500M -t 10 测试结果 ------------------------------------------------------------ Client connecting to 192.168.56.106, UDP port 5001 Sending 1470 byte d…

全面监测健康数据,更实用的健康手表,dido E55S Pro上手

关心健康的朋友&#xff0c;一般都特别关注自己的各项健康数据&#xff0c;会通过智能手表之类的工具来持续检测。现在健康类的智能手表选择很多&#xff0c;功能也很丰富&#xff0c;像是我现在用的这款dido E55S Pro&#xff0c;除了常规的心率、血氧之外&#xff0c;还检测心…

vector【实现】:迭代器失效以及非法的间接寻址、深拷贝中的浅拷贝。

vector模拟实现_云的小站的博客-CSDN博客 目录 主题&#xff1a; 迭代器失效 Insert导致的迭代器失效 ereas导致的迭代器失效 非法的间接寻址 深拷贝中的浅拷贝。 主题&#xff1a; 1&#xff09;迭代器失效 2&#xff09;非法的间接寻址 3&#xff09;深拷贝中的浅拷…

2023年最佳SleekFlow最佳替代品

建立更强大的对话关系的最佳平台是什么&#xff1f;现如今&#xff0c;国内客服集成与营销自动化工具也有非常多&#xff0c;比如SaleSmartly&#xff08;ss客服&#xff09;&#xff0c;被称为SleekFlow的最佳替代品。了解为什么SaleSmartly是SleekFlow的最佳替代品。在这篇文…

BTC交易费激增,LTC活跃地址数飙升! BRC-20爆火背后,区块链网络经历了什么?

BRC-20 代币和 Ordinals 协议的日益普及推动了对比特币区块空间的需求&#xff0c;比特币区块链的费用已飙升至两年来的高点。 BRC-20代币标准在 Ordinals 协议上运行。Ordinals 允许用户通过将对数字艺术的引用写入基于比特币的小型交易中&#xff0c;来将数据嵌入比特币区块…

网站历史快照查询软件-批量网站历史快照查询

批量网站历史快照查询软件 批量网站历史快照查询软件是一种可以让用户在短时间内批量查询多个网站历史快照的工具&#xff0c;可以极大地提高用户的工作效率。批量实时查询是该软件的一大优势&#xff0c;下面主要介绍批量实时查询的优势。 一、高效性 批量实时查询可以同时…

通过2种Python库,教会你如何在自动化测试时加入进度条?

前言 我们在执行自动化测试或者调试时&#xff0c;自动化测试用例数量过多&#xff0c;不清楚目前用例数执行了多少个了&#xff0c;还差多少个执行完成。 这时候就会猜想&#xff0c;如果执行过程中存在进度条&#xff0c;就很清楚的了解到测试用例的执行情况&#xff0c;今…

Cannot read properties of null (reading ‘content‘)报错解决

项目是用vue3webpack&#xff0c;始终启动不成功~ 一、问题报错 二、报错解决尝试总结 &#xff08;1&#xff09;首先尝试的是因为我近期在做vite3vue3的需求把node版本升到了 16.17.1 猜测是不是node版本影响的 node版本切了14.15.3&#xff0c;16.17.1&#xff0c;以及很…

【换根DP+容斥】P3047 [USACO12FEB]Nearby Cows G

P3047 [USACO12FEB]Nearby Cows G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a; 思路&#xff1a; 做法就是换根 预处理dp[v][j]用普通的树形DP处理即可 注意&#xff1a;一开始预处理的dp[v][j]指的是在v的子树里离v为j的权值和 Code&#xff1a; #in…

JavaWeb12-三大组件之过滤器-Filter

1. 官方文档 文档&#xff1a;java_ee_api_中英文对照版.chm 2. Filter 过滤器说明 2.1 为啥要过滤器-需求示意图 ● 一图胜千言 2.2 过滤器介绍 Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)Filter 过滤器是 JavaEE 的规范…

图像处理:手写实现图像增广算法(旋转、亮度调整、裁剪与拼接)

前言 图像增广算法在计算机视觉领域扮演着至关重要的角色。随着深度学习的兴起&#xff0c;大规模数据集的需求变得更加迫切&#xff0c;而图像增广算法可以通过对原始图像进行一系列变换&#xff0c;扩充数据集&#xff0c;从而提升模型的泛化能力和鲁棒性。 本文将着重介绍…

win10系统cpu版本 Tensorflow2.5.0的安装

文章目录 前言电脑重装系统了&#xff0c;顺便简单记录一下我的tensorflow2.5.0 CPU的安装过程 一、创建一个虚拟环境&#xff1f;二、确定 输入 y三、激活你的环境四、安装tensorflow2.5.0五、利用清华镜像源加速一下&#xff0c;不然等到猴年马月&#xff01;六&#xff0c;开…

Shape-E:文字到3D的生成模型试用

文章目录 Shape-E&#xff1a;文字到3D的生成模型试用项目介绍项目地址项目使用试用Text to 3DImage to 3D 总结 Shape-E&#xff1a;文字到3D的生成模型试用 项目介绍 Shape-E是一个生成3D模型的工具&#xff0c;可以通过输入文字或者上传图片生成3D模型。该模型的项目地址是…

穿越火线(CF) AI 自瞄 代码 权重 数据集 亲测可用(结尾有资源)

初衷 本人热衷玩CF&#xff0c;同时为一名程序员&#xff0c;近期听说AI霸占FPS游戏&#xff0c;本着学习的态度&#xff0c;特来测试 不喜欢看过程的小伙伴直接看最下面 模型 采用yolov5模型架构 对过程感兴趣的小伙伴下文自行学习 https://zhuanlan.zhihu.com/p/17212138…

数字孪生技术在矿业领域怎样应用?

随着科技的不断发展&#xff0c;数字孪生技术正逐渐走入矿业领域&#xff0c;为这个传统行业带来了全新的变革和机遇。数字孪生技术以其精准模拟和实时监控的特性&#xff0c;为矿业企业提供了更高效、更安全的运营和管理方式。 在矿业开采过程中&#xff0c;数字孪生技术的应…

代码随想录算法训练营day42 | 01背包问题,你该了解这些!,01背包问题,你该了解这些! 滚动数组 , 416. 分割等和子集

代码随想录算法训练营day42 | 背包理论基础&#xff0c;背包理论基础&#xff08;滚动数组&#xff09;&#xff0c; 416. 分割等和子集 1、01背包理论基础背包问题概述01背包二维dp数组01背包案例 2、01背包理论基础&#xff08;滚动数组&#xff09;3、 416. 分割等和子集解…

Redis持久化-Redis主从-Redis哨兵-Redis分片集群

主要内容 Redis持久化Redis主从Redis哨兵Redis分片集群 Redis持久化 Redis有两种持久化的方案: RDB持久化AOF持久化 1. RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所…