1. 程式人生 > >解決在Filter中讀取Request中的流後, 然後再Control中讀取不到的做法

解決在Filter中讀取Request中的流後, 然後再Control中讀取不到的做法

摘要: 大家知道, StringMVC中@RequestBody是讀取的流的方式, 如果在之前有讀取過流後, 發現就沒有了.

我們來看一下核心程式碼: filter中主要做的事情, 就是來校驗請求是否合法, 是否有篡改過值.

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        if (Boolean.valueOf(authentication)) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            if (BasicdataCommonsConstant.POST.equalsIgnoreCase(httpServletRequest.getMethod())) {
                // 防止流讀取一次後就沒有了, 所以需要將流繼續寫出去
                ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
                String body = HttpHelper.getBodyString(requestWrapper);
                if (StringUtils.isBlank(body)) {
                    LOGGER.error("非法請求, 沒有APP_KEY, APP_SECRET");
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_FROMTYPE)));
                    return;
                }
                Map<String, String> parameters = gson.fromJson(body, new TypeToken<Map<String, String>>() {
                }.getType());

                String APP_KEY = parameters.get("appKey");
                String APP_SECRET = parameters.get("appSecret");

                TAuthUser authUser = authUserMap.get(APP_KEY);
                if (authUser == null) {
                    LOGGER.error("非法請求, 沒有APP_KEY, APP_SECRET");
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_FROMTYPE)));
                    return;
                } else if (StringUtils.isBlank(APP_SECRET)) {
                    LOGGER.error("非法請求, APP_SECRET為空, user={}", gson.toJson(authUser));
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_FROMTYPE_AND_KEY_CANNT_BE_NULL)));
                    return;
                } else if (!APP_SECRET.equals(authUser.getAppSecret())) {
                    LOGGER.error("非法請求: 沒有APP_KEY, APP_SECRET不匹配. user={}, password={}, name={}", APP_KEY, APP_SECRET, authUser.getName());
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_KEY)));
                    return;
                }
                String SIGNATURE = parameters.get("signature");
                // 對引數進行簽名
                String md5 = SignatureUtil.decryptSignature(parameters);
                if (!md5.equals(SIGNATURE)) {
                    LOGGER.error("非法請求, signature ={}", SIGNATURE);
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_SIGNATURE_ERROR)));
                    return;
                }
                threadLocalUser.setAuthUser(authUser);
                chain.doFilter(requestWrapper, response);
            }
        }
        chain.doFilter(request, response);
    }


大家都知道, 流只能讀一次, 讀了就沒有了, 為了後面的程式碼還能夠取得流, 我們應該還需要將其寫出去才行.

所以, 我新建立了一個類. 看程式碼:

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;
import java.nio.charset.Charset;

/**
 * Created with antnest-platform
 * User: chenyuan
 * Date: 12/31/14
 * Time: 8:49 PM
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final 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) {

            }
        };
    }
}

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created with antnest-platform
 * User: chenyuan
 * Date: 12/24/14
 * Time: 10:39 AM
 */
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();
    }

}


請注意這裡的編碼, 最好將其轉換成UTF-8的編碼格式, 不然你獲取到的中文則會使亂碼的. 我自己也習慣於UTF-8的編碼.

這樣子就應該差不多了哦~