Java替换RequestBody和RequestParam参数的属性

Java替换RequstBody和RequestParam参数的属性

本文主要讲解在Java环境中如何替换RequestBody和RequestParam参数中的属性

背景

近期由于接手的老项目中存在所有接口中新增一个加密串来给接口做一个加密效果(项目历史原因,不方便上Jwt授权这套),所以就研究了一下Http请求链路,发现可以通过 javax.servlet.Filter去实现

替换RequestParam参数

首先通过继续HttpServletRequestWrapper 来达到获取和替换RequestParam中的参数信息,接下来我们通过javax.servlet.Filter 去获取ServletRequest中参数的信息,并且定义对应规则,来实现替换参数

代码示例:

java 复制代码
package com.simplemessage.cloudpayservice.infrastructure.config.http;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

/**
 * @CreateAt: 2023/10/24 12:13
 * @ModifyAt: 2023/10/24 12:13
 * @Version 1.0
 */
public class MyRequestWrapper  extends HttpServletRequestWrapper {

    private Map params = new HashMap<>();
    public MyRequestWrapper(HttpServletRequest request, Map newParams) {
        super(request);
        if(request.getParameterMap() != null){
            this.params.putAll(request.getParameterMap());
        }
        if(newParams != null){
            this.params.putAll(newParams);
        }
    }

    /**
     * 获取参数
     * @return
     */
    @Override
    public Map getParameterMap() {
        return params;
    }

    @Override
    public Enumeration getParameterNames() {
        Vector l = new Vector(params.keySet());
        return l.elements();
    }


    @Override
    public String[] getParameterValues(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) {
            return (String[]) v;
        } else if (v instanceof String) {
            return new String[]{(String) v};
        } else {
            return new String[]{v.toString()};
        }
    }

    /**
     * 根据参数的key获取参数
     * @param name
     * @return
     */
    @Override
    public String getParameter(String name) {
        Object v = params.get(name);
        if (v == null) {
            return null;
        } else if (v instanceof String[]) {
            String[] strArr = (String[]) v;
            if (strArr.length > 0) {
                return strArr[0];
            } else {
                return null;
            }
        } else if (v instanceof String) {
            return (String) v;
        } else {
            return v.toString();
        }
    }
}
java 复制代码
package com.simplemessage.cloudpayservice.infrastructure.config.http;

import com.fasterxml.jackson.core.io.JsonEOFException;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.RequestFacade;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @CreateAt: 2023/10/24 12:16
 * @ModifyAt: 2023/10/24 12:16
 * @Version 1.0
 */
@Slf4j
@Component
@WebFilter(filterName = "replaceGetRequestFilter", urlPatterns = {"/*"})
public class ReplaceGetRequestFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long start = System.currentTimeMillis();
        //获取HttpServletRequest对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        //判断当前是否为Get请求
        if ("GET".equalsIgnoreCase(httpServletRequest.getMethod())) {
        	// 获取参数信息
            String param= request.getParameter("param");
            //判断参数是否为空,为空则放行
            if (StringUtils.isEmpty(param)) {
                chain.doFilter(request, response);
                return;
            } else {
                Map<String, String[]> newParameterMap = new HashMap<>();
                // 替换参数(自定义规则)
                String newParama="test";
                newParameterMap.put("param", newParama);
                // 实现参数替换
                MyRequestWrapper myRequestWrapper = new MyRequestWrapper(httpServletRequest, newParameterMap);
                chain.doFilter(myRequestWrapper, response);
            }

        } else {
            try {
                chain.doFilter(request, response);
            } catch (HttpMessageNotReadableException httpMessageNotReadableException) {
                log.error(((RequestFacade) request).getRequestURI() + ", " + httpMessageNotReadableException.getMessage());
            } catch (JsonEOFException jsonEOFException) {
                log.error(((RequestFacade) request).getRequestURI() + ", " + jsonEOFException.getMessage());
            }
        }
        long end = System.currentTimeMillis();
        log.info("{} 接口耗时:{} ms", httpServletRequest.getRequestURI(), (end - start));
    }

    @Override
    public void destroy() {
    }
}

替换RequestBody参数

主要思路就是通过获取Post中请求的输入流信息,解析输入流信息,按照对应的规则进行替换参数信息,最后将对应的流信息包装进行返回

代码示例:

java 复制代码
package com.simplemessage.cloudpayservice.infrastructure.config.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @version 1.0
 * @createAt: 2023/10/24 12:23:23
 * @modifyAt: 2023/10/24 12:23:23
 */
@RestControllerAdvice
@Slf4j
public class DecryptRequestBodyHandler implements RequestBodyAdvice {
    
    /**
     * 该方法用于判断当前请求,是否要执行beforeBodyRead方法
     * methodParameter方法的参数对象
     * type方法的参数类型
     * aClass 将会使用到的Http消息转换器类类型
     * 注意:此判断方法,会在beforeBodyRead 和 afterBodyRead方法前都触发一次。
     * @return 返回true则会执行beforeBodyRead
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * 在Http消息转换器执转换,之前执行
     * inputMessage 客户端请求的信息
     * parameter 参数信息
     * targetType 参数类型
     * converterType Http消息转换器类类型
     *
     * @return 返回 一个自定义的HttpInputMessage
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        // 如果body是空内容直接返回原来的请求
        if (inputMessage.getBody().available() <= 0) {
            return inputMessage;
        }
        // 请求中的header信息
        HttpHeaders headers = inputMessage.getHeaders();     
      
        // 将输入流读出来,注意 body 里面的流只能读一次
        ByteArrayOutputStream requestBodyDataByte = new ByteArrayOutputStream();
        try {
        	//复制流信息
            IOUtils.copy(inputMessage.getBody(), requestBodyDataByte);
        } catch (Exception e) {
            log.error("参数流拷贝失败: ", e.toString());
            return inputMessage;
        }
        ByteArrayOutputStream requestBodyDataByteNew = null;
        try {
            JSONObject body = JSON.parseObject(requestBodyDataByte.toByteArray(), JSONObject.class);
            if (ObjectUtils.isEmpty(body)) {
                return inputMessage;
            }
            //自定义规则西悉尼
            if (body.containsKey("param")) {
                String custId = body.getString("param"); 
                String newParam="";              
                body.put("custId", newParam);
                requestBodyDataByteNew = new ByteArrayOutputStream();
                //拷贝流信息
                IOUtils.copy(new ByteArrayInputStream(body.toJSONString().getBytes()), requestBodyDataByteNew);
            }
        } catch (Throwable e) {
            log.error("流转换异常 ", e.toString());
        }
        // 如果上述发生异常,仍然使用原来的请求内容
        requestBodyDataByte = requestBodyDataByteNew != null ? requestBodyDataByteNew : requestBodyDataByte;
        InputStream rawInputStream = new ByteArrayInputStream(requestBodyDataByte.toByteArray());
        inputMessage.getHeaders().set(HttpHeaders.CONTENT_LENGTH, String.valueOf(rawInputStream.available()));
        return new HttpInputMessage() {
            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }

            @Override
            public InputStream getBody() throws IOException {
                return rawInputStream;
            }
        };
    }

    /**
     * 在Http消息转换器执转换,之后执行
     * body 转换后的对象
     * inputMessage 客户端的请求数据
     * parameter handler方法的参数类型
     * targetType handler方法的参数类型
     * converterType 使用的Http消息转换器类类型
     *
     * @return 返回一个新的对象
     */
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    /**
     * 参数与afterBodyRead相同,不过这个方法body为空的情况
     */
    @Override
    public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }
}

如有哪里讲得不是很明白或是有错误,欢迎指正

如您喜欢的话不妨点个赞收藏一下吧🙂

相关推荐
simple_ssn26 分钟前
【蓝桥杯】压缩字符串
java·算法
舒克日记41 分钟前
Java:189 基于SSM框架的在线电影评价系统
java·开发语言
2401_857610031 小时前
中文学习系统:成本效益分析与系统优化
java·数据库·学习·架构
nbsaas-boot1 小时前
如何更高效地使用乐观锁提升系统性能
java·服务器·数据库
转转技术团队1 小时前
【述职黑话】ToB交易业务解决方案之状态机
java·状态模式
darkdragonking1 小时前
解决POM依赖与maven仓库关联的问题
java·maven
m0_672449601 小时前
前后端分离(前端删除数据库数据)
java·数据库·mysql
飞的肖1 小时前
在 Java 项目中集成和使用 dl4j 实现通过扫描图片识别快递单信息
java·ai·图像识别·dl4j
Javatutouhouduan1 小时前
如何系统全面地自学Java语言?
java·后端·程序员·编程·架构师·自学·java八股文
正在绘制中1 小时前
Java重要面试名词整理(八):RabbitMQ
java·面试·java-rabbitmq