客户端传递json格式数据,spring mvc服务端接受并进行参数校验

江枫思渺然 提交于 2019-12-02 19:58:07

前端传来的参数是json格式的数据,并不是传统的表单提交,由于服务端使用spring mvc框架,首先想到了Spring mvc 自带的@RequestBody注解,直接将传递参数注入到处理方法的参数中,
但是这样遇到了一个问题,在使用拦截器对客户端传递参数进行校验时是没法直接通过request.getParameter("name")获取该对象的参数,只能通过request.getInputStream();将传递对象的body读出,解析json字符串,对参数进行处理,
拦截器的方法跑通了,到了具体的处理方法时,注入参数报错了

原因是,之前的在拦截器中的处理方法已经通过request.getInputStream()取出了输入流,参数解析时已经拿不到了,此时想到两种解决方法:

1.将所有的参数解析及参数校验逻辑写在每个方法中

2.让request.getInputStream()再次取值时还能取到

第一种方法,能够很快实现,但是会产生大量的重复代码

于是决定使用第二种方法,代码如下

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对象进行包装

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

参数校验拦截器

在web.xml中配置时,将该过滤器放在最后,实践中使用了shiro框架,放在前边时造成了错误

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!