【设计模式】装饰者模式解决实际问题

1.问题描述

改造一个十年老项目,目的是防sql注入和XSS攻击。

难点在于前后端传参类型十分复杂,有application/json的,有x-www-form-urlencoded的。

2.解决思路

最开始想使用filter,统一拦截下请求体,然后过滤实现。

但是对于application/json可以这么做,因为就是个json,但是对于x-www-form-urlencoded,它每个请求的key都不一样,不好拿。

解决问题的思路,找到这两种请求类型的共同点。

  1. 都是放到request里的。
  2. 使用时,application/json的本质是把请求中的流转化为预先设置好的对象,x-www-form-urlencoded的本质是一个放到request里的map。

根据以上两点,可以使用装饰模式,把request包起来,然后在包装类上对流和map进行处理,然后再把这个包装类当做request放到请求链中。

3.方案实践

public class ParameterRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    private Map<String, Object> parameterMap; 
    
	public ParameterRequestWrapper(HttpServletRequest request) {
		super(request);
        //在这里,把request中的流读出来,转成String
		String sessionStream = getBodyString(request);
        //针对x-www-form-urlencoded,由于后续调用使用的是getParameter这种形式从request的map中取值.
        //针对application/json,我们把这个String转成字符数组。
        if("x-www-form-urlencoded".reqeust.getContentType){
            parameterMap =reqest.getParametes()
        }else if("application/json".reqeust.getContentType){
             body = sessionStream.getBytes(Charset.forName("UTF-8"));
        }else{
            //根据项目中还出现什么妖魔鬼怪,来决定怎么组织数据结构接request里的值
        }
		
       
	}

	@Override
	public String getParameter(String name) {
		String parameter = null;
		String value = null;
		if(parameterMap != null && parameterMap.get(name) !=null) {
			value =(String) parameterMap.get(name);
		}
		parameter = xssEncode(value);
		// 过滤sql注入
		parameter = filterContent(parameter);
		return parameter;
	}

	/**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(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();
                }
            }
        }
        String ssb = xssEncode(sb.toString());
		// 过滤sql注入
        ssb = filterContent(ssb);
        System.out.println("application/json的StringBuilder"+sb.toString());
        return ssb;
    }
 
    /**
     * 复制输入流
     */
    public InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }
    @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 {
                //@RequestBody会使用这个方法获取流,然后转成Bean给Controller使用,所以要把body写到这里来。
                return bais.read();
            }
 
            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

}

原文地址:https://www.cnblogs.com/pandaNHF/p/15678154.html