@RequestBody參數已經被讀取,到底是何緣由?

不知道大家有沒有對用戶輸入的東西進行過敏感校驗,若是不進行校驗,用戶屬於一些攻擊腳本,那麼咱們的服務就掛逼啦!因此咱們首先須要經過過濾器將用戶的數據讀出來進行安全校驗,這裏面涉及到一個動做,就是須要將用戶的數據在過濾器中讀出來,進行校驗,經過以後再放行。java

問題

若是咱們的數據是get請求倒還好,可是若是是一些數據量比較大,咱們須要經過post json的方式來講傳遞數據的時候,這個時候實際上是經過流的方式傳遞的,若是在過濾器中將參數讀取出來以後,而後放行,等到到Servlet的時候,@RequestBody是沒法獲取到數據的,由於post json使用流傳遞,流被讀取以後就不存在了,因此咱們在過濾器中讀取以後,@ReqeustBody天然就讀不到數據了,同時會報以下一個錯誤。json

  • 在過濾器中讀取body中的數據
@WebFilter
@Slf4j
public class CheckUserFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        // 在過濾器中讀取數據
        BufferedReader reader = request.getReader();

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();

        System.out.println(sb.toString());

        filterChain.doFilter(request, res);
    }
}
  • 出現異常,就是說內容已經被讀取了,你不能調用了
{	"id":"1",	"username":"bingfeng"}
java.lang.IllegalStateException: UT010003: Cannot call getInputStream(), getReader() already called
	at io.undertow.servlet.spec.HttpServletRequestImpl.getInputStream(HttpServletRequestImpl.java:666)
	at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)
	at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)

解決

  • HttpServletRequestWrapper

那麼出現這種問題怎麼辦呢?能不能經過一箇中間的變量將這些數據保存下來,而後咱們就能夠一直讀取了,這樣不就解決了這個問題了嗎?那保存在哪裏呢?這個時候 HttpServletRequestWrapper 就排上用場了。數組

這個其實你能夠把它理解爲Request的包裝類,Reqeust中有的方法它都有,咱們經過繼承這個類,重寫該類中的方法,將body中的參數保存一個byte數組中,而後放行的時候將這個包裝類傳遞進去,不就能夠一直拿到參數了?安全

  • 封裝Request類
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    /**
     * 全部參數的集合
     */
    private Map<String, String[]> parameterMap;


    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        BufferedReader reader = request.getReader();
        body = readBytes(reader);
        parameterMap = request.getParameterMap();
    }


    @Override
    public BufferedReader getReader() throws IOException {

        ServletInputStream inputStream = getInputStream();

        if (null == inputStream) {
            return null;
        }

        return new BufferedReader(new InputStreamReader(inputStream));
    }

    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<>(parameterMap.keySet());
        return vector.elements();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        if (body == null) {
            return null;
        }

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    /**
     * 經過BufferedReader和字符編碼集轉換成byte數組
     *
     * @param br
     * @return
     * @throws IOException
     */
    private byte[] readBytes(BufferedReader br) throws IOException {
        String str;
        StringBuilder retStr = new StringBuilder();
        while ((str = br.readLine()) != null) {
            retStr.append(str);
        }
        if (StringUtils.isNotBlank(retStr.toString())) {
            return retStr.toString().getBytes(StandardCharsets.UTF_8);
        }
        return null;
    }
}
  • 將過濾器改造
@WebFilter
@Slf4j
public class CheckUserFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);

        // 從Request的包裝類中讀取數據
        BufferedReader reader = requestWrapper.getReader();

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();

        System.out.println(sb.toString());

        filterChain.doFilter(requestWrapper, res);
    }
}

通過這樣的配置以後,咱們即便在過濾器中獲取了參數,請求也會到達Servlet。app

若是基礎知識IO那塊不是很紮實的話,第一眼看到這個問題確實挺懵逼的。我也是百度以後解決的,確實值得記錄一下,有時候咱們會對全部請求進來的參數進行保存輸出什麼的,這個時候若是是post json數據的話,若是不是特別明白,可能也會出現這種問題。ide

<p style="text-align:center;font-weight:bold;color:#0e88eb;font-size:20px">日拱一卒,功不唐捐</p>post

<p style="text-align:center;font-weight:bold;color:#773098;font-size:16px">更多內容請關注</p>ui

相關文章
相關標籤/搜索