HttpServletRequestWrapper的使用:二次封装/处理Request

ResponseWrapper同理

背景

有段程序使用Http的方式与业务方交互,而且是明文传输数据。我方的代码已经打包放在服务器上运行了很长时间,这时突然要求修改数据传输的方式,要求加密后再传输,而我方的原有的代码不能改变,以防止引发其它问题。

如何在不修改我方现有的代码的前提下,满足合作商的要求?

技术方案

采用Filter+HttpServletRequestWrapper就可以解决这个问题。
首先:在filter中拦截到加密后的请求,将参数解密,然后组装成一个新的明文请求串。
然后:重写HttpServletRequestWrapper中的getInputStream()方法,让其返回过滤器解析后的明文串即可。

写两个一摸一样的servlet,一个用来直接接收合作商的明文请求并打印;一个用来接收Filter处理后的合作商的请求并打印(Filter中将合作商加密后的参数解密再传给这个Servlet)。

@WebServlet("/SiServlet")  
public class SiServlet extends HttpServlet {  
    private static final long serialVersionUID = 1L;  
  
    /** 
     * @see HttpServlet#HttpServlet() 
     */  
    public SiServlet() {  
        super();  
    }  
  
    /** 
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 
     *      response) 
     */  
    protected void doGet(HttpServletRequest request,  
            HttpServletResponse response) throws ServletException, IOException {  
        this.doPost(request, response);  
    }  
  
    /** 
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 
     *      response) 
     *  
     */  
    protected void doPost(HttpServletRequest request,  
            HttpServletResponse response) throws ServletException, IOException {  
        String bizBindMsg = IOUtils.toString(request.getInputStream(), "UTF-8");  
        bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");  
        System.out.println("SiServlet接收到请求为: " + bizBindMsg);  
  
        response.getWriter().write("==========success=========");  
    }  
}  
@WebServlet("/SiServletNormal")  
public class SiServletNormal extends HttpServlet {  
    private static final long serialVersionUID = 1L;  
  
    /** 
     * @see HttpServlet#HttpServlet() 
     */  
    public SiServletNormal() {  
        super();  
    }  
  
    /** 
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 
     *      response) 
     */  
    protected void doGet(HttpServletRequest request,  
            HttpServletResponse response) throws ServletException, IOException {  
        this.doPost(request, response);  
    }  
  
    /** 
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 
     *      response) 
     *  
     */  
    protected void doPost(HttpServletRequest request,  
            HttpServletResponse response) throws ServletException, IOException {  
        String bizBindMsg = IOUtils.toString(request.getInputStream(), "UTF-8");  
        bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");  
        System.out.println("SiServletNormal接收到请求为: " + bizBindMsg);  
  
        response.getWriter()  
                .write("==========SiServletNormal Success=========");  
    }  
}  

然后我使用HttpClient模拟了一下合作商发送明文和密文请求的过程,加密使用Base64简单模拟一下。

public class AdcClient {  
    private HttpPost httpPost = null;  
    private HttpClient client = null;  
    private List<NameValuePair> pairs = null;  
  
    public AdcClient() {  
        httpPost = new HttpPost("http://localhost:8080/filtertest/SiServlet");  
        client = new DefaultHttpClient();  
    }  
  
    /** 
     * 发送明文消息 
     *  
     */  
    public void sendMsg() {  
  
        try {  
            httpPost = new HttpPost(  
                    "http://localhost:8080/filtertest/SiServletNormal");  
  
            pairs = new ArrayList<NameValuePair>();  
            pairs.add(new BasicNameValuePair(("param1"), "obama没加密"));  
            pairs.add(new BasicNameValuePair(("param2"), "男没加密"));  
            pairs.add(new BasicNameValuePair(("param3"), "汉没加密"));  
            pairs.add(new BasicNameValuePair(("param4"), "山东没加密"));  
  
            httpPost.setEntity(new UrlEncodedFormEntity(pairs, "UTF-8"));  
            // httpPost.setHeader("Cookie", "TOKEN=1234567890");  
            HttpResponse response = client.execute(httpPost);  
  
            HttpEntity entity = response.getEntity();  
            BufferedReader br = new BufferedReader(new InputStreamReader(  
                    entity.getContent()));  
            String line = null;  
            StringBuffer result = new StringBuffer();  
            while ((line = br.readLine()) != null) {  
                result.append(line);  
                line = br.readLine();  
            }  
  
            System.out.println("来自SiServletNormal的响应为:" + result.toString());  
        } catch (UnsupportedEncodingException e) {  
            e.printStackTrace();  
        } catch (ClientProtocolException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * 发送加密后的消息 
     */  
    public void sendEncryptMsg() {  
        try {  
            pairs = new ArrayList<NameValuePair>();  
            pairs.add(new BasicNameValuePair(("param1"), Base64EnDecrypt  
                    .base64Encode("obama")));  
            pairs.add(new BasicNameValuePair(("param2"), Base64EnDecrypt  
                    .base64Encode("男")));  
            pairs.add(new BasicNameValuePair(("param3"), Base64EnDecrypt  
                    .base64Encode("汉")));  
            pairs.add(new BasicNameValuePair(("param4"), Base64EnDecrypt  
                    .base64Encode("山东")));  
  
            HttpEntity reqEntity = new UrlEncodedFormEntity(pairs, "UTF-8");  
            httpPost.setEntity(reqEntity);  
            // httpPost.setHeader("Cookie", "TOKEN=1234567890");  
            HttpResponse response = client.execute(httpPost);  
  
            /** 
             * 获取响应信息 
             */  
            HttpEntity entity = response.getEntity();  
            BufferedReader br = new BufferedReader(new InputStreamReader(  
                    entity.getContent()));  
            String line = null;  
            StringBuffer result = new StringBuffer();  
            while ((line = br.readLine()) != null) {  
                result.append(line);  
                line = br.readLine();  
            }  
  
            System.out.println("来自SiServlet的响应为:" + result.toString());  
        } catch (UnsupportedEncodingException e) {  
            e.printStackTrace();  
        } catch (ClientProtocolException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * @param args 
     * @throws UnsupportedEncodingException 
     */  
    public static void main(String[] args) throws UnsupportedEncodingException {  
        new AdcClient().sendMsg();  
  
        new AdcClient().sendEncryptMsg();  
    }  
}  

重点是下面的这个HttpServletRequestWrapper,我重写了它的getInputStream()方法,这个方法返回包含明文的ServletInputStream

public class MyRequestWrapper extends HttpServletRequestWrapper {  
    private HttpServletRequest request;  
  
    public MyRequestWrapper(HttpServletRequest request) {  
        super(request);  
        this.request = request;  
    }  
  
    /** 
     * 先解密,获取明文;然后将明文转化为字节数组;然后再去读取字节数组中的内容 
     */  
    @Override  
    public ServletInputStream getInputStream() {  
        String bizBindMsg = null;  
        ServletInputStream stream = null;  
  
        try {  
            stream = request.getInputStream();  
            bizBindMsg = IOUtils.toString(stream, "UTF-8");  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        try {  
            bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");  
        } catch (UnsupportedEncodingException e) {  
            e.printStackTrace();  
        }  
        System.out.println("MyRequestWrapper接收到的请求为: " + bizBindMsg);  
  
        /** 
         * 获取加密的值进行解密 
         */  
        final StringBuffer reqStr = new StringBuffer();  
        reqStr.append("param1=").append(  
                Base64EnDecrypt.base64Decode(bizBindMsg.substring(  
                        bizBindMsg.indexOf("param1=") + 7,  
                        bizBindMsg.indexOf("param2="))));  
        reqStr.append("&");  
        reqStr.append("param2=").append(  
                Base64EnDecrypt.base64Decode(bizBindMsg.substring(  
                        bizBindMsg.indexOf("param2=") + 7,  
                        bizBindMsg.indexOf("param3="))));  
        reqStr.append("&");  
        reqStr.append("param3=").append(  
                Base64EnDecrypt.base64Decode(bizBindMsg.substring(  
                        bizBindMsg.indexOf("param3=") + 7,  
                        bizBindMsg.indexOf("param4="))));  
        reqStr.append("&");  
        reqStr.append("param4=").append(  
                Base64EnDecrypt.base64Decode(bizBindMsg.substring(bizBindMsg  
                        .indexOf("param4=") + 7)));  
  
        System.out.println("********MyRequestWrapper接收到的解密后的请求为*********");  
        System.out.println(reqStr.toString());  
  
        /** 
         * 将解密后的明文串放到buffer数组中 
         */  
        byte[] buffer = null;  
        try {  
            buffer = reqStr.toString().getBytes("UTF-8");  
        } catch (UnsupportedEncodingException e) {  
            e.printStackTrace();  
        }  
        final ByteArrayInputStream bais = new ByteArrayInputStream(buffer);  
  
        ServletInputStream newStream = new ServletInputStream() {  
  
            @Override  
            public int read() throws IOException {  
                return bais.read();  
            }  
        };  
        return newStream;  
    }  
}  

最后是简单的Filter,在这里将加密后的ServletRequest重新包装,交给SiServlet进行处理

public class EncryptFilter implements Filter {  
  
    @Override  
    public void destroy() {  
    }  
  
    @Override  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  
  
        chain.doFilter(new MyRequestWrapper((HttpServletRequest) request),  
                response);  
    }  
  
    @Override  
    public void init(FilterConfig arg0) throws ServletException {  
  
    }  
  
}  

我的web.xml中是这样配置的

<filter>  
    <filter-name>encryptFilter</filter-name>  
    <filter-class>com.test.filter.EncryptFilter</filter-class>  
  </filter>  
  <filter-mapping>  
    <filter-name>encryptFilter</filter-name>  
    <url-pattern>/SiServlet</url-pattern>  
  </filter-mapping>  

确保过滤器entyptFilter只拦截到SiServlet的请求即可。

运行AdcClient,可以看到下面的结果
在这里插入图片描述

这里的重点是MyRequestWrapper中重写的getInputStream()方法。大家可以看看API中关于HttpServletRequest的用法http://tomcat.apache.org/tomcat-5.5-doc/servletapi/index.html。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HttpServletRequestWrapper是一个装饰器模式的实现,它允许我们在不改变原始请求的情况下,对请求进行修改。在Interceptor中,我们可以通过HttpServletRequestWrapper来修改request参数。 首先,我们需要自定义一个类继承HttpServletRequestWrapper,重写getParameter方法,以实现修改request参数的功能。具体实现如下: ``` public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { private Map<String, String[]> params = new HashMap<>(); public MyHttpServletRequestWrapper(HttpServletRequest request) { super(request); this.params.putAll(request.getParameterMap()); } @Override public String getParameter(String name) { String[] values = params.get(name); if (values == null || values.length == 0) { return null; } return values[0]; } @Override public Map<String, String[]> getParameterMap() { return params; } @Override public Enumeration<String> getParameterNames() { return Collections.enumeration(params.keySet()); } public void addParameter(String name, String value) { if (params.containsKey(name)) { String[] values = params.get(name); values = Arrays.copyOf(values, values.length + 1); values[values.length - 1] = value; params.put(name, values); } else { params.put(name, new String[]{value}); } } } ``` 接下来,在Interceptor中,我们可以通过如下代码来使用MyHttpServletRequestWrapperrequest参数进行修改: ``` public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getMethod().equalsIgnoreCase("get")) { // 使用MyHttpServletRequestWrapper对请求参数进行修改 MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(request); requestWrapper.addParameter("key", "value"); // 将修改后的request传给下一个Interceptor或Controller request = requestWrapper; } return true; } ``` 上述代码中,我们先判断请求的方法是否为get,如果是,则使用MyHttpServletRequestWrapper对请求参数进行修改,然后将修改后的request传给下一个Interceptor或Controller。如果请求方法不为get,则直接将原始request传给下一个Interceptor或Controller。 需要注意的是,使用HttpServletRequestWrapperrequest参数进行修改时,只有在下一个Interceptor或Controller真正使用request参数时,修改后的参数才会生效。如果在当前Interceptor中进行了修改,但在下一个Interceptor或Controller中并没有使用修改后的参数,那么修改就会失效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值