客戶端傳遞json格式數據,spring mvc服務端接受並進行參數校驗

前端傳來的參數是json格式的數據,並非傳統的表單提交,因爲服務端使用spring mvc框架,首先想到了Spring mvc 自帶的@RequestBody註解,直接將傳遞參數注入處處理方法的參數中,
可是這樣遇到了一個問題,在使用攔截器對客戶端傳遞參數進行校驗時是無法直接經過request.getParameter("name")獲取該對象的參數,只能經過request.getInputStream();將傳遞對象的body讀出,解析json字符串,對參數進行處理,
攔截器的方法跑通了,到了具體的處理方法時,注入參數報錯了前端

緣由是,以前的在攔截器中的處理方法已經經過request.getInputStream()取出了輸入流,參數解析時已經拿不到了,此時想到兩種解決方法:java

1.將全部的參數解析及參數校驗邏輯寫在每一個方法中git

2.讓request.getInputStream()再次取值時還能取到web

第一種方法,可以很快實現,可是會產生大量的重複代碼spring

因而決定使用第二種方法,代碼以下json

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

public class BodyServletRequest extends HttpServletRequestWrapper {

    private byte[] body;

    public BodyServletRequest(HttpServletRequest request) {
        super(request);
        try {
            body = getBodyString(request).getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        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 readListener) {

            }

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

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

    private 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();
    }
}

使用HttpServletRequestWrapper 作一個包裝類數組

import com.hotel.system.support.http.BodyServletRequest;
import com.hotel.system.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class RequestWrapperFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

        if (request.getContentType() != null && request.getContentType().contains("json")) {
            request = new BodyServletRequest((HttpServletRequest) request);
            this.logUserRequestInfo((HttpServletRequest) request);
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }

    private void logUserRequestInfo(HttpServletRequest request) {
        log.info("request url" + request.getRequestURI());
        log.info("contentType" + request.getContentType());
        log.info("http body" + HttpUtils.getContent(request));
    }
}

使用RequestWrapperFilter對request對象進行包裝mvc

@Slf4j
public class NeedSignInterceptor extends BaseInterceptor {

    @Value("${isDev}")
    private String isDev;

    private String appKey = "04c4b414a75949ad9908771e6167d07c";

    private static final int TIMESTAMP_EXPIRE = 360000;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (Boolean.parseBoolean(isDev)) {
            return true;
        }
        HandlerMethod handlerMethod = null;
        if (handler instanceof HandlerMethod) {
            //輸出方法請求參數
            handlerMethod = (HandlerMethod) handler;
            logMethodInfo(handlerMethod, request);
            NeedSign needAppSign = null;
            needAppSign = handlerMethod.getBean().getClass().getAnnotation(NeedSign.class);
            if (needAppSign == null) {
                needAppSign = handlerMethod.getMethodAnnotation(NeedSign.class);
            }
            if (needAppSign != null && needAppSign.isNeed()) {
                //簽名驗證邏輯
                JSONObject paramsJsonObject = HttpUtils.getJsonObject(request);
                Map<String, String> params = HttpUtils.getParamsMap(paramsJsonObject);
                TreeMap<String, String> treeMap = new TreeMap<>(params);
                log.info("interceptor params :" + treeMap.toString());
                String timeStamp = treeMap.get("timestamp");
                Date date = new Date(Long.parseLong(timeStamp));

                if (date.getTime() - (System.currentTimeMillis()) > TIMESTAMP_EXPIRE) {
//                    errorResponse(response, ResponseType.TIMEOUT);
                }
                String sign = params.get("sign");
                if (!isSignAllow(treeMap, sign)) {
                    errorResponse(response, ResponseType.SIGN_ERROR);
                    return false;
                }
            }
        }
        return true;
    }

    private boolean isSignAllow(TreeMap<String, String> map, String sign) {
        if (StringUtils.isNotEmpty(sign)) {
            if (StringUtils.isNotEmpty(appKey) && sign.trim().equalsIgnoreCase(getSign(map, appKey))) {
                return true;
            }
        }
        return false;
    }

    private String getSign(TreeMap<String, String> params, String appkey) {
        StringBuilder sig = new StringBuilder();
//        for (String value : params.values()) {
//            sig.append(value);
//        }
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (!"sign".equals(entry.getKey())) {
                sig.append(entry.getValue());
            }
        }
        sig.append(appkey);
        try {
            String sign = getMd5str(sig.toString().getBytes("UTF-8"));
            log.info("sig string : " + sig.toString());
            log.info("sign: " + sign);
            return sign;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 取字節數組的md5值的十六進制串表示
     */
    public String getMd5str(byte[] b) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            return bytetoHexString(md.digest(b)).toLowerCase();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 將字節數組,轉換爲字符串
     */
    private String bytetoHexString(byte[] data) {
        char[] hexdigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f'};
        char[] tem = new char[data.length * 2];
        for (int i = 0; i < data.length; i++) {
            byte b = data[i];
            tem[i * 2] = hexdigits[b >>> 4 & 0x0f];
            tem[i * 2 + 1] = hexdigits[b & 0x0f];
        }
        return new String(tem);
    }

    private void logMethodInfo(HandlerMethod handlerMethod, HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();
        Map<String, String[]> params = request.getParameterMap();
        for (Map.Entry<String, String[]> entry : params.entrySet()) {
            sb.append(entry.getKey()).append(":").append(Arrays.toString(entry.getValue())).append(" ");
        }
        log.info(handlerMethod.getMethod().getName() + sb.toString());
    }
}

參數校驗攔截器app

在web.xml中配置時,將該過濾器放在最後,實踐中使用了shiro框架,放在前邊時形成了錯誤框架

相關文章
相關標籤/搜索