欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

完美解決request請求流只能讀取一次的問題

 更新時間:2020年08月24日 09:31:13   作者:如來神掌十八式  
這篇文章主要介紹了完美解決request請求流只能讀取一次的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

解決request請求流只能讀取一次的問題

實際開發(fā)碰到的問題

解決request請求流中的數(shù)據(jù)二次或多次使用問題

實際開發(fā)碰到的問題

springboot項目中,為了防止sql注入,采用Filter攔截器對所有請求流中的json數(shù)據(jù)進行校驗,請求數(shù)據(jù)沒問題則繼續(xù)向下執(zhí)行,在后邊的代碼中應用到請求參數(shù)值時,發(fā)現(xiàn)request中的json數(shù)據(jù)為空;

除上邊描述的情況,嘗試過兩次從request中獲取json數(shù)據(jù),第二次同樣是獲取不到的。

解決request請求流中的數(shù)據(jù)二次或多次使用問題

繼承HttpServletRequestWrapper,將請求體中的流copy一份,覆寫getInputStream()和getReader()方法供外部使用。每次調(diào)用覆寫后的getInputStream()方法都是從復制出來的二進制數(shù)組中進行獲取,這個二進制數(shù)組在對象存在期間一直存在,這樣就實現(xiàn)了流的重復讀取。

//增強類
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
  //保存流
  private byte[] requestBody = null;
 
  public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    requestBody = StreamUtils.copyToByteArray(request.getInputStream());
  }
 
  @Override
  public ServletInputStream getInputStream() throws IOException {
 
    final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
 
    return new ServletInputStream() {
 
      @Override
      public int read() throws IOException {
        return bais.read();
      }
 
      @Override
      public boolean isFinished() {
        return false;
      }
 
      @Override
      public boolean isReady() {
        return false;
      }
 
      @Override
      public void setReadListener(ReadListener readListener) {
 
      }
    };
  }
 
  @Override
  public BufferedReader getReader() throws IOException{
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }
} 
 
//過濾器
@Component
@WebFilter
public class RequestSqlValidFilter implements Filter {
  private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
 
  }
 
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest)request);
 
    //請求參數(shù)合法,無sql注入
    if((sqlValid(request, response))){
      chain.doFilter(requestWrapper, response);//requestWrapper中保存著供二次使用的請求數(shù)據(jù)
    }else {
      logger.error("RequestSqlValidFilter sqlValid param error");
    }
  }
 
  @Override
  public void destroy() {
 
  }

補充知識:【java web】解決流讀完一次就不能再次獲取body數(shù)據(jù)的問題

問題來自我工作業(yè)務上的需求:前端請求時需要將json用RSA算法加密,數(shù)據(jù)經(jīng)過后端過濾器進行自動解密,這樣做的好處是以后不需要在每一個方法里都手動解密一次,增加代碼的簡潔性、可維護性。

但這樣一來便會面臨一個問題:http的request請求的輸入流在過濾器中就已經(jīng)被讀取了(因為需要讀取并解密request body 里被前端加密了的json數(shù)據(jù)),流只能被讀取一次,這樣一來數(shù)據(jù)便傳不進controller里,導致接下來的業(yè)務無法進行。

于是我上網(wǎng)找了一些資料并成功解決了這個問題,基本思路是封裝原生的HttpServletRequest請求,將其輸入流里的數(shù)據(jù)保存在字節(jié)數(shù)組里,最后重寫getInputStream方法,使其之后每次讀取數(shù)據(jù)都是從字節(jié)數(shù)組里讀取的。

第一步:寫一個Request包裝類BodyReaderHttpServletRequestWrapper

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
 
  private byte[] body;
 
  public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
  }
 
  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }
 
  @Override
  public ServletInputStream getInputStream() throws IOException {
 
    final ByteArrayInputStream bais = new ByteArrayInputStream(body);
 
    return new ServletInputStream() {
 
      @Override
      public int read() throws IOException {
        return bais.read();
      }
 
      @Override
      public boolean isFinished() {
        return false;
      }
 
      @Override
      public boolean isReady() {
        return false;
      }
 
      @Override
      public void setReadListener(ReadListener readListener) {
 
      }
 
    };
  }
 
  public void setInputStream(byte[] body){
    this.body = body;
  }

里面涉及一個HttpHelper類,順便也貼出來

public class HttpHelper {
 
  /**
   * 獲取請求Body
   * @param request
   * @return
   */
  public static String getBodyString(ServletRequest request) {
    StringBuilder sb = new StringBuilder();
    InputStream inputStream = null;
    BufferedReader reader = null;
    try {
      inputStream = request.getInputStream();
      reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
      String line = "";
      while ((line = reader.readLine()) != null) {
        sb.append(line);
      }
    } 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();
  }  
}

第二步:編寫包裝Filter

這個filter類可以將經(jīng)過的原生Request請求自動包裝成BodyReaderHttpServletRequestWrapper

/**
 * desc : 用于包裝原生request, 解決流讀完一次就不能再次獲取body數(shù)據(jù)的問題
 * Created by Lon on 2018/3/9.
 */
public class RequestWrapperFilter implements Filter{
 
  private static final Logger LOGGER = LoggerFactory.getLogger(RequestWrapperFilter.class);
 
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
 
  }
 
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    ServletRequest requestWrapper = null;
    if(request instanceof HttpServletRequest) {
      requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
    }
    if(null == requestWrapper) {
      LOGGER.error("包裝request失敗!將返回原來的request");
      chain.doFilter(request, response);
    } else {
      LOGGER.info("包裝request成功");
      chain.doFilter(requestWrapper, response);
    } 
  }
 
  @Override
  public void destroy() {
 
  }
}

第三步:在web.xml上配置過濾器

 <filter>
  <filter-name>RequestWrapperFilter</filter-name>
  <filter-class>com.kx.security.filter.RequestWrapperFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>RequestWrapperFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

這里要注意的是這個包裝過濾器可能要寫在web.xml配置文件里某些過濾器的前面,比如解密過濾器,否則被解密過濾器先讀取流的話,包裝過濾器就讀取不了流了。

至此一個簡單的解決方法就完成啦!

以上這篇完美解決request請求流只能讀取一次的問題就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

最新評論