Java替換RequstBody和RequestParam引數的屬性

vbcjnmkk發表於2024-09-01

Java替換RequstBody和RequestParam引數的屬性

本文主要講解在Java環境中如何替換RequestBody和RequestParam引數中的屬性
背景
近期由於接手的老專案中存在所有介面中新增一個加密串來給介面做一個加密效果(專案歷史原因,不方便上Jwt授權這套),所以就研究了一下Http請求鏈路,發現可以透過 javax.servlet.Filter去實現

替換RequestParam引數
首先透過繼續HttpServletRequestWrapper來達到獲取和替換RequestParam中的引數資訊,接下來我們透過javax.servlet.Filter去獲取ServletRequest中引數的資訊,並且定義對應規則,來實現替換引數
程式碼示例:

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();
      }
      }
      }
      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.FilterConfig;
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

@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", new String[]{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() {
}

}

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**

  • 注入bean
  • @CreateAt: 2023/10/30 9:57
  • @ModifyAt: 2023/10/30 9:57
  • @Version 1.0
    */

@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new ReplaceGetRequestFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}

替換RequestBody引數
主要思路就是透過獲取Post中請求的輸入流資訊,解析輸入流資訊,按照對應的規則進行替換引數資訊,最後將對應的流資訊包裝進行返回
程式碼示例:

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> aClass) {
    return true;
    }
    //程式碼效果參考:http://www.intpipe.com/sitemap/post.xml
    /**

    • 在Http訊息轉換器執轉換,之前執行

    • inputMessage 客戶端請求的資訊

    • parameter 引數資訊

    • targetType 引數型別

    • converterType Http訊息轉換器類型別

    • @return 返回 一個自定義的HttpInputMessage
      */
      @Override
      public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> 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> converterType) {
      return body;
      }

    /**

    • 引數與afterBodyRead相同,不過這個方法body為空的情況
      */
      @Override
      public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) {
      return body;
      }
      }

相關文章