请求参数完整性校验,解决流只能写一次的问题

背景:一个app的后台开发,现在想要对请求内容进行校验,防止请求内容被人篡改,目前的做法是前端发送请求时,对参数进行MD5摘要算法(中间加了些我们自己约定的规则),将算出来的MD5值附在请求里一起带过来,后台拿着前台传过来的参数进行相同规则的计算,将计算出来的MD5值和前台计算的值进行比较。

想法没什么问题,然而开发时发现在拦截器里获取到的已经是mapper后的对象了,拿不到原始请求的json串,自然也不好比较算出来的MD5值了。

查了下,前台是post请求数据是json类型,那request.getparameter()之类的就拿不到值了,要获得原始请求的json串,只能去流里面读了,那流读完一次就没法读了,最开始的想法是能不能在读流的地方,把原始请求留个备份啥的。debug跟了下整个请求处理的源码(spring 那一套),找到最终是在com.fasterxml.jackson.databind包里的ObjectMapper类里面去解析了流,并转成对应的对象。这是jackson-databind jar包的方法,

修改重打jar包肯定不现实,想了下本地建个同名的包,再建个同名的ObjectMapper类,自然就可以把原来jar包里的ObjectMapper类给覆盖掉了,试了下,可行。姑且算方案一吧。但这方法还是不太好,要建一个与项目不相干的包。

方案二,那我能想到的还是得想办法解决流只能读一次这个问题了,那就得想办法是不是可以把流再写回去,查了下,参考了网上的办法。

也就是在通过加过滤器,在过滤器里,把流读出来后,通过重写HttpServletRequestWrapper的getInputStream的方法,再把流的内容写回去。代码网上很多,大同小异。

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body =  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) {

            }
        };
    }


    public static String getBodyString(ServletRequest request) {
        try {
            return StreamUtil.readText(request.getInputStream());
        } catch (IOException e1) {
            throw ReqErrCodes.CUSTOM_PROMPT.exception("流内容解析失败");
        }
    }
}

 

@Order(1)
@WebFilter(filterName = "checkSignFilter", urlPatterns = "/*")
public class CheckSignFilter implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		System.out.println("CheckSignFilter");
		HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
	    ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpRequest);
	    
	    
	    String str = BodyReaderHttpServletRequestWrapper.getBodyString(requestWrapper);
	    HttpServletRequest request = null;
	    if(servletRequest instanceof HttpServletRequest){
	    	request = (HttpServletRequest)servletRequest;
	    }
	    String md5code = request.getHeader("MD5Code");
            String MD5ecode = MD5.getMessageDigest(str);
	    if(!md5code.equals(MD5ecode)){
	    	// TODO
	    }
	    
		filterChain.doFilter(requestWrapper, servletResponse);
	}

	@Override
	public void destroy() {

	}
}

 

posted @ 2018-01-15 18:23  boogieman  阅读(645)  评论(0编辑  收藏  举报