SpringBoot 攔截器中校驗Json數據


SpringBoot 攔截器中校驗Json數據

背景

做開發的時候,經常會使用@RequestBody注解,這個注解是非常的好用。但是如果你想在請求參數傳到后台的

時候做一個參數檢驗,當然可以!使用SpringMVC的攔截器,在攔截器里把request的數據讀取出來不就行

了!!,但是在使用了攔截器的時候會出現一個問題!!!!你在攔截器讀取了request的數據,在Controller里

面@RequestBody注解獲取Json就會失敗就讀取不到數據!!!!那就是RequestBody是流的形式讀取的,流讀

取一次就沒有了!!

為什么使用RequestBody只能讀取一遍請求數據流?

那是因為流對應的是數據,數據放在內存中,有的是部分放在內存中。read 一次標記一次當前位置(mark

position),第二次read就從標記位置繼續讀(從內存中copy)數據。 所以這就是為什么讀了一次第二次是空

了。 怎么讓它不為空呢?只要inputstream 中的pos 變成0就可以重寫讀取當前內存中的數據。javaAPI中有一個

方法public void reset() 這個方法就是可以重置pos為起始位置,但是不是所有的IO讀取流都可以調用該方法!

ServletInputStream是不能調用reset方法,這就導致了只能調用一次getInputStream()。

案例:在SpringBoot中校驗json值

如果controller類中使用的是json值,需要在攔截器中進行值校驗,就必須使用request將值從拿到。

但是使用request.getParameter()是無法獲取json數據的。

從request中取出json的工具類

使用下面的工具類,可以將request中的json數據取出:

package cn.rayfoo.common.util.json;

import com.alibaba.fastjson.JSONObject;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p> 將http請求中的request數據轉換為json </p>
 * @date 2020/8/6 19:02
 */
public class GetRequestJsonUtil {
    public static JSONObject getRequestJsonObject(HttpServletRequest request) throws IOException {
        String json = getRequestJsonString(request);
        return JSONObject.parseObject(json);
    }
    /***
     * 獲取 request 中 json 字符串的內容
     *
     * @param request
     * @return : <code>byte[]</code>
     * @throws IOException
     */
    public static String getRequestJsonString(HttpServletRequest request)
            throws IOException {
        String submitMehtod = request.getMethod();
        // GET
        if (submitMehtod.equals("GET")) {
            return new String(request.getQueryString().getBytes("iso-8859-1"),"utf-8").replaceAll("%22", "\"");
            // POST
        } else {
            return getRequestPostStr(request);
        }
    }

    /**
     * 描述:獲取 post 請求的 byte[] 數組
     * <pre>
     * 舉例:
     * </pre>
     * @param request
     * @return
     * @throws IOException
     */
    public static byte[] getRequestPostBytes(HttpServletRequest request)
            throws IOException {
        int contentLength = request.getContentLength();
        if(contentLength<0){
            return null;
        }
        byte buffer[] = new byte[contentLength];
        for (int i = 0; i < contentLength;) {

            int readlen = request.getInputStream().read(buffer, i,
                    contentLength - i);
            if (readlen == -1) {
                break;
            }
            i += readlen;
        }
        return buffer;
    }

    /**
     * 描述:獲取 post 請求內容
     * <pre>
     * 舉例:
     * </pre>
     * @param request
     * @return
     * @throws IOException
     */
    public static String getRequestPostStr(HttpServletRequest request)
            throws IOException {
        byte buffer[] = getRequestPostBytes(request);
        String charEncoding = request.getCharacterEncoding();
        if (charEncoding == null) {
            charEncoding = "UTF-8";
        }
        return new String(buffer, charEncoding);
    }
}

從request中取出json中的內容

   ...
   @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //獲取json數據
        JSONObject json = GetRequestJsonUtil.getRequestJsonObject(request);
       
        //獲取json中具體的參數
        String name = json.getString("name");
        String password = json.getString("password");
        String phoneNumber = json.getString("phoneNumber");
        String email = json.getString("email");
        String code = json.getString("code");
       
       //校驗
        .....
}
...

問題:此時攔截器執行后request中的數據丟失

解決Request流自動關閉導致數據丟失

解決辦法:重寫HttpServletRequestWrapper方法

這種方法就是通過重寫HttpServletRequestWrapper把request的保存下來,然后通過過濾器保存下來的request

在填充進去,這樣就可以多次讀取request了

創建下述類:

package cn.rayfoo.common.util.json;

import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p></p>
 * @date 2020/8/6 20:49
 */
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private byte[] requestBody = null;

    public MyHttpServletRequestWrapper (HttpServletRequest request) {

        super(request);

        //緩存請求body
        try {
            requestBody = StreamUtils.copyToByteArray(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 重寫 getInputStream()
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if(requestBody == null){
            requestBody= new byte[0];
        }
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }

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

    /**
     * 重寫 getReader()
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

加入過濾器,包裝request

package cn.rayfoo.common.filter;

import cn.rayfoo.common.util.json.MyHttpServletRequestWrapper;

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



/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p>創建一個實現Filter的類,重寫doFilter方法,將ServletRequest替換為自定義的request類 </p>
 * @date 2020/8/6 20:53
 */
@WebFilter(urlPatterns = "/*",filterName = "requestReplaced")
public class HttpServletRequestReplacedFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest) {
            requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if(requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }


    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }
}

在啟動器上加入下面的注解,掃描filter

@ServletComponentScan

重寫編寫攔截器

在攔截器中,使用myWrapper代替request

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //封裝request
        MyHttpServletRequestWrapper myWrapper= new MyHttpServletRequestWrapper(request);
        //獲取json數據
        JSONObject json = GetRequestJsonUtil.getRequestJsonObject(myWrapper);



        //獲取前端參數
        String name = json.getString("name");
        String password = json.getString("password");
        String phoneNumber = json.getString("phoneNumber");
        String email = json.getString("email");
        String code = json.getString("code");
        
        //過濾參數
        ...
        }

別忘了注冊攔截器哦,此時,就可以正常的在攔截器中校驗json數據啦~

注:切面在攔截器之后執行,如果攔截器進行了攔截,切面將無法被執行

本文部分內容參考了CSDN帖中的內容


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM