美文网首页
重写Request,修改Request中的Body内容

重写Request,修改Request中的Body内容

作者: 茶礼宜世 | 来源:发表于2019-11-06 21:40 被阅读0次

    今天介绍在服务端如何修改前端传过来的body数据。
    有个场景是这样子,前端请求参数是以下的json数据,放在body里面,我们需要在过滤器对sign签名进行验证,如果签名通过了,就只截取data字段下的json数据给具体的controller,如果不通过,直接响应给前端错误信息。

    {
        "clientId":"xxxxxx",
        "timestamp":"154515462",
        "sign":"yyyyyy",
        "data":{
            "id":1000,
            "name":"zhangsan"
        }
    }
    

    由于HttpServletRequest中的body数据只能get不能set,即不能重新赋值,并且只能读取一次,但是我们的场景却是需要对body重新赋值,这个时候需要我们想办法重写HttpServletRequest,方案就是我们自定义一个类继承HttpServletRequestWrapper,HttpServletRequestWrapper本身是继承了ServletRequestWrapper并实现了HttpServletRequest,故可作为request在过滤器的filterChain中传递,以达到我们的目的。
    我们的自定义类需要重写getReader()和getInputStream()两个方法,在返回时重新读取我们自定义的body。

        /**
         * 重写request,获取body数据的时候读取新的body
         **/
        private class ModifyBodyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    
            // 重新赋值的body数据
            private String bodyJsonStr;
    
            public ModifyBodyHttpServletRequestWrapper(HttpServletRequest request, String bodyJsonStr) {
                super(request);
                this.bodyJsonStr = bodyJsonStr;
            }
    
            @Override
            public ServletInputStream getInputStream() throws IOException {
                if(StringUtils.isEmpty(bodyJsonStr)) {
                    bodyJsonStr = "";
                }
                // 必须指定utf-8编码,否则json请求数据中如果包含中文,会出现异常
                final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bodyJsonStr.getBytes("utf-8"));
                ServletInputStream servletInputStream = 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 byteArrayInputStream.read();
                    }
                };
                return servletInputStream;
            }
    
            @Override
            public BufferedReader getReader() throws IOException {
                return new BufferedReader(new InputStreamReader(this.getInputStream()));
            }
    
            public String getBodyJsonStr() {
                return bodyJsonStr;
            }
    
            public void setBodyJsonStr(String bodyJsonStr) {
                this.bodyJsonStr = bodyJsonStr;
            }
        }
    }
    

    定义一个过滤器,实现Filter接口,并将过滤器配置到web.xml中,对匹配的url进行拦截。

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.google.common.collect.Maps;
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.*;
    import java.util.Map;
    
    /**
     * 授权验证过滤器
     */
    public class AuthorizeFilter implements Filter {
    
        private static Logger logger = LoggerFactory.getLogger(AuthorizeFilter.class);
    
        @Autowired
        private AuthorizeService authorizeService;
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException { }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            // 读取前端传来的body数据
            JSONObject requestJsonObject = getRequestParamJson(request);
            // 验证sign
            AuthorizeStatusVo statusVo = checkRequestData(requestJsonObject);
            if(ErrorCode.SUCCESS.getStatus() == statusVo.getRet()) {
                // 如果验证成功,截取data下的数据重新赋值到request的body中,将新的request作为参数传递给doFilter()方法
                JSONObject dataJson = requestJsonObject.getJSONObject("data");
                String dataStr = dataJson == null ? "" : dataJson.toString();
                ModifyBodyHttpServletRequestWrapper httpServletRequestWrapper = new ModifyBodyHttpServletRequestWrapper((HttpServletRequest)request, dataStr);
                chain.doFilter(httpServletRequestWrapper, response);
            } else {
                try {
                    // 如果验证失败,直接响应给前端错误信息
                    returnJson(response, statusVo);
                } catch (Exception e) {
                    logger.error("授权认证过滤器异常:request=" + requestJsonObject, e);
                }
            }
        }
    
        // 获取request中的body数据
        private JSONObject getRequestParamJson(ServletRequest request) {
             // TODO 自己实现
        }
    
        // 检查请求数据并对sign验证
        private AuthorizeStatusVo checkRequestData(JSONObject requestJsonObject) {
            // TODO 自己实现
        }
    
        private void returnJson(ServletResponse response, AuthorizeStatusVo statusVo) throws Exception{
            String json = JSON.toJSONString(statusVo);
            PrintWriter writer = null;
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html; charset=utf-8");
            try {
                writer = response.getWriter();
                writer.print(json);
    
            } catch (IOException e) {
                logger.error("response error",e);
            } finally {
                if (writer != null)
                    writer.close();
            }
        }
    
        @Override
        public void destroy() { }
    

    在web.xml中配置过滤器

    <filter>
           <filter-name>authorizeFilter</filter-name>
           <filter-class>xxx.xxx.AuthorizeFilter</filter-class>
    </filter>
    <filter-mapping>
           <filter-name>authorizeFilter</filter-name>
           <url-pattern>/api/**</url-pattern>
    </filter-mapping>
    

    到这里,我们的需求就已经实现了,如果sign验证成功,在controller中接收到的请求参数就是以下内容:

    {
            "id":1000,
            "name":"zhangsan"
    }
    

    谢谢大家的支持,如果存在错误的地方,请评论指出,大家一起学习~~

    相关文章

      网友评论

          本文标题:重写Request,修改Request中的Body内容

          本文链接:https://www.haomeiwen.com/subject/ibcebctx.html