最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

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

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

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

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

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

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

//增強(qiáng)類
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() {
 
  }

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

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

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

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

第一步:寫一個(gè)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;
  }

里面涉及一個(gè)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

這個(gè)filter類可以將經(jīng)過的原生Request請求自動(dòng)包裝成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>

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

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

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

相關(guān)文章

  • SpringAOP 如何通過JoinPoint獲取參數(shù)名和值

    SpringAOP 如何通過JoinPoint獲取參數(shù)名和值

    這篇文章主要介紹了SpringAOP 通過JoinPoint獲取參數(shù)名和值的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • java實(shí)現(xiàn)簡單的學(xué)生信息管理系統(tǒng)代碼實(shí)例

    java實(shí)現(xiàn)簡單的學(xué)生信息管理系統(tǒng)代碼實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)簡單的學(xué)生信息管理系統(tǒng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 給你的MyBatis-Plus裝上批量插入的翅膀(推薦)

    給你的MyBatis-Plus裝上批量插入的翅膀(推薦)

    這篇文章主要介紹了給你的MyBatis-Plus裝上批量插入的翅膀,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 簡單了解Spring Framework5.0新特性

    簡單了解Spring Framework5.0新特性

    這篇文章主要介紹了簡單了解Spring Framework5.0新特性,涉及了核心框架修訂,核心容器更新,使用Kotlin進(jìn)行函數(shù)式編程等幾個(gè)方面的介紹,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • SpringBoot項(xiàng)目docker容器部署實(shí)現(xiàn)

    SpringBoot項(xiàng)目docker容器部署實(shí)現(xiàn)

    本文主要介紹了SpringBoot項(xiàng)目docker容器部署實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-03-03
  • Spring詳細(xì)講解事務(wù)失效的場景

    Spring詳細(xì)講解事務(wù)失效的場景

    實(shí)際項(xiàng)目開發(fā)中,如果涉及到多張表操作時(shí),為了保證業(yè)務(wù)數(shù)據(jù)的一致性,大家一般都會(huì)采用事務(wù)機(jī)制,好多小伙伴可能只是簡單了解一下,遇到事務(wù)失效的情況,便會(huì)無從下手,下面這篇文章主要給大家介紹了關(guān)于Spring事務(wù)失效場景的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • Spring Boot報(bào)錯(cuò):No session repository could be auto-configured, check your configuration的解決方法

    Spring Boot報(bào)錯(cuò):No session repository could be auto-configured

    這篇文章主要給大家介紹了關(guān)于Spring Boot報(bào)錯(cuò):No session repository could be auto-configured, check your configuration的解決方法,文中給出了詳細(xì)的解決方法,對遇到這個(gè)問題的朋友們具有一定參考價(jià)值,需要的朋友下面來一起看看吧。
    2017-07-07
  • 一文搞懂Java中的注解和反射

    一文搞懂Java中的注解和反射

    這篇文章主要給大家介紹了關(guān)于Java中注解和反射的原理及使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Spring中的@Conditional注解使用和原理詳解

    Spring中的@Conditional注解使用和原理詳解

    這篇文章主要介紹了Spring中的@Conditional注解使用和原理詳解,@Conditional在Spring4.0中被引入,用于開發(fā)"If-Then-Else"類型的bean注冊條件檢查,在@Conditional之前,也有一個(gè)注解@Porfile起到類似的作用,需要的朋友可以參考下
    2024-01-01
  • Disconf實(shí)現(xiàn)分布式配置管理的原理與設(shè)計(jì)

    Disconf實(shí)現(xiàn)分布式配置管理的原理與設(shè)計(jì)

    這篇文章主要為大家介紹了Disconf實(shí)現(xiàn)分布式配置管理的原理與設(shè)計(jì)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03

最新評論

甘泉县| 喀喇| 和顺县| 新闻| 繁峙县| 镇赉县| 兴仁县| 九台市| 嵊泗县| 浮梁县| 乐昌市| 安阳市| 安仁县| 招远市| 台山市| 潜山县| 江源县| 白水县| 安塞县| 淳安县| 锦州市| 河池市| 修水县| 于田县| 扬中市| 游戏| 县级市| 钟祥市| 象山县| 思南县| 太和县| 黄石市| 克拉玛依市| 安平县| 平湖市| 正宁县| 虞城县| 普宁市| 普洱| 凤阳县| 武义县|