`

servlet请求参数和返回页面内容的处理(HttpServletRequestWrapper与HttpServletResponseWrapper)

阅读更多

        Servlet规范中的filter引入了一个功能强大的拦截模式。Filter能在request到达servlet的服务方法之前拦截HttpServletRequest对象,而在服务方法转移控制后又能拦截HttpServletResponse对象。

        你可以使用filter来实现特定的任务,比如验证用户输入、请求参数以及压缩web内容等操作。还可以在response输出页面内容之前,进行页面内容的过滤等操作。

1. HttpServletRequestWrapper

        因为java.util.Map所包装的HttpServletRequest对象的参数是不可改变的。这极大地缩减了filter的应用范围。如果在HttpServletRequest对象到达Struts的action  servlet之前,我们可以通过一个filter将用户输入的多余空格去掉,难道不是更美妙吗?这样的话,你就不必等到在Struts的action表单验证方法中才进行这项工作了。

        幸运的是,可以使用avax.servlet.http.HttpServletRequestWrapper类来装饰HttpServletRequest对象。覆写getParameterMap()、getParameterValues()、getParameter()等方法来实现对请求参数的处理。

        这在许多servlet/JSP应用中是很有用的,包括Struts及JavaServer  Faces等应用。例如,Struts通过调用HttpServletRequest对象的getParameterValues()对象来处理action表单。通过覆盖装饰类中此方法,你可以改变当前HttpServletRequest对象的状态。 

        要创建HttpServletRequest的装饰类,你需要继承HttpServletRequestWrapper并且覆盖你希望改变的方法。 

public class MyRequestWrapper extends HttpServletRequestWrapper
{

	/**
	 * 规范化后请求参数map
	 */
	private Map<String, String[]> sanitized;
	/**
	 * 原始请求参数map
	 */
	private Map<String, String[]> orig;
	
	@SuppressWarnings("unchecked")
	public MyRequestWrapper(HttpServletRequest req) 
	{
		super(req);
		orig = req.getParameterMap();	
		sanitized = getParameterMap();
	}		

	@Override
	public String getParameter(String name) 
	{		
		String[] vals = getParameterMap().get(name); 
		if (vals != null && vals.length > 0) 
			return vals[0];
		else        
			return null;        
	}


	@SuppressWarnings("unchecked")
	@Override
	public Map<String, String[]> getParameterMap() 
	{	
		if (sanitized==null)
			sanitized = sanitizeParamMap(orig);
		return sanitized;			

	}

	@Override
	public String[] getParameterValues(String name)
	{	
		return getParameterMap().get(name);
	}


	/**
	 * 规范请求参数
	 * @param raw
	 * @return
	 */
	private  Map<String, String[]> sanitizeParamMap(Map<String, String[]> raw) 
	{		
		Map<String, String[]> res = new HashMap<String, String[]>();
		if (raw==null)
			return res;
	
		for (String key : (Set<String>) raw.keySet())
		{			
			String[] rawVals = raw.get(key);
			String[] snzVals = new String[rawVals.length];
			for (int i=0; i < rawVals.length; i++) 
			{
				snzVals[i] = xssEncode(rawVals[i]);
			}
			res.put(key, snzVals);
		}			
		return res;
	}
	
	/**
	 * 将特殊字符替换为全角
	 * @param s
	 * @return
	 */
	private  String xssEncode(String s) {
		if (s == null || s.isEmpty()) {
			return s;
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			switch (c) {
			case '>':
				sb.append('>');// 全角大于号
				break;
			case '<':
				sb.append('<');// 全角小于号
				break;
			case '\'':
				sb.append('‘');// 全角单引号
				break;
			case '\"':
				sb.append('“');// 全角双引号
				break;
			case '&':
				sb.append('&');// 全角&
				break;
			case '\\':
				sb.append('\');// 全角斜线
				break;
			case '/':
				sb.append('/');// 全角斜线
				break;
			case '#':
				sb.append('#');// 全角井号
				break;
			case '(':
				sb.append('(');// 全角(号
				break;
			case ')':
				sb.append(')');// 全角)号
				break;
			default:
				sb.append(c);
				break;
			}
		}
		return sb.toString();
	}
}

 2. HttpServletResponseWrapper

        可以在response输出页面内容之前,进行页面内容的过滤等操作。

比如知名的页面装饰框架sitemesh,就是利用filter过滤器先截获返回给客户端的页面,然后分析html代码并最终装饰页面效果后返回给客户端。

        要截获页面返回的内容,整体的思路是先把原始返回的页面内容写入到一个字符Writer,然后再组装成字符串并进行分析,最后再返回给客户端。

public class ResponseWrapper extends HttpServletResponseWrapper{
	private PrintWriter cachedWriter;
	private CharArrayWriter bufferedWriter;

	public ResponseWrapper(HttpServletResponse response) {
		super(response);
		// 这个是我们保存返回结果的地方
		bufferedWriter = new CharArrayWriter();
		// 这个是包装PrintWriter的,让所有结果通过这个PrintWriter写入到bufferedWriter中
		cachedWriter = new PrintWriter(bufferedWriter);
	}

	@Override
	public PrintWriter getWriter() {
		return cachedWriter;
	}

	/**
	 * 获取原始的HTML页面内容。
	 * 
	 * @return
	 */
	public String getResult() {
		return bufferedWriter.toString();
	}
}

    然后再写一个过滤器来截获内容并处理:

public class MyServletFilter implements Filter {
	@Override
	public void destroy() {
		// TODO Auto-generated method stub

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		// 使用我们自定义的响应包装器来包装原始的ServletResponse
		ResponseWrapper wrapper = new ResponseWrapper(
				(HttpServletResponse) response);
		// 这句话非常重要,注意看到第二个参数是我们的包装器而不是response
		chain.doFilter(request, wrapper);
		// 处理截获的结果并进行处理,比如替换所有的“名称”为“铁木箱子”
		String result = wrapper.getResult();
		result = result.replace("名称", "铁木箱子");
		// 重置响应输出的内容长度
		response.setContentLength(-1);
		// 输出最终的结果
		PrintWriter out = response.getWriter();
		out.write(result);
		out.flush();
		out.close();
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub

	}
}

        有可能在运行的过程中页面只输出一部分,尤其是在使用多个框架后(比如sitemesh)出现的可能性非常大,在探究了好久之后终于发现原来是响应的ContentLength惹的祸。因为在经过多个过滤器或是框架处理后,很有可能在其他框架中设置了响应的输出内容的长度,导致浏览器只根据得到的长度头来显示部分内容。知道了原因,处理起来就比较方便了,我们在处理结果输出前重置一下ContentLength即可

// 重置响应输出的内容长度
		response.setContentLength(-1);
		// 输出最终的结果
		PrintWriter out = response.getWriter();
		out.write(result);
		out.flush();
		out.close();

        这样处理后就不会再出现只出现部分页面的问题了!

小结 

        Servlet  filter可以在调用一个servlet的服务方法后,拦载或加工HTTP请求。尽管这非常诱人,但其实际使用却有所限制,因为你不能改变HttpServletRequest对象。 

        这时候装饰模式派上了用场。本文演示了如何通过应用装饰模式来“修改”HttpServletRequest对象,从而使你的servlet  filter更加有用。在上面filter例子中,filter改了request参数中的用户输入,而这一点,如果没有装饰request对象,你是无论如何也不可能做到的。 

分享到:
评论

相关推荐

    使用HttpServletRequestWrapper在filter修改request参数

    NULL 博文链接:https://rensanning.iteye.com/blog/1706208

    filter对request请求拦截,对请求参数进行修改

    对request请求进行拦截,对请求参数修改。常用于前台提交表单参数关键字的过滤。此工具可以对参数拦截并转义后提交到对应的处理类。 除了添加两个JsFilter.java和GetHttpServletRequestWrapper.java之外,需要在web....

    servlet-api-2.4.jar.zip

    javax.servlet.Filter javax.servlet.Servlet javax.servlet.FilterChain javax.servlet.http.Cookie javax.servlet.FilterConfig javax.servlet.ServletConfig javax.servlet.GenericServlet javax.servlet....

    javax.servlet.jar下载

    Files contained in javax.servlet.jar: META-INF/MANIFEST.MF javax/servlet/http/LocalStrings.properties javax.servlet.http.HttpSessionBindingListener.class javax.servlet....

    servlet2.4doc

    Called by the servlet container to indicate to a servlet that the servlet is being taken out of service. destroy() - Method in class javax.servlet.GenericServlet Called by the servlet container to ...

    HttpServletRequestWrapper

    这是一个关于HttpServletRequestWrapper使用的列子,工作需要,所以传上来的。

    HttpServletRequestWrapper应用(二):包装文件上传请求

    NULL 博文链接:https://jiaguwen123.iteye.com/blog/714139

    JAVA J2EE 类库文档

    J2EE 类库文档 ...HttpServletRequestWrapper HttpServletResponse HttpServletResponseWrapper HttpSession HttpSessionActivationListener HttpSessionAttributeListener HttpSessionBindingEvent ...

    java过滤器对所有参数去除前后空格

    对项目中的所有参数去除前后空格过滤,统一处理参数!可以基于此过滤器实现过滤跨站脚本攻击,参数的增加,修改!敏感词汇过滤。实现原理为重写HttpServletRequestWrapper,获取参数的方法。include和 Forwarded ...

    HttpServletRequestWrapper 用法

    NULL 博文链接:https://fishhappy365.iteye.com/blog/484185

    java servlet 监听器

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { MyRequest myRequest = null; HttpServletRequest request2 = ...

    通用签名SDK源码+使用教程.zip

    通过继承HttpServletRequestWrapper,配置过滤器的方式实现签名校验 目前只支持post请求类型签名校验 只会对请求体的参数验签 对于重要业务接口,前端请求时需要加入随机字符串,当前时间戳和秘钥(盐)对业务参数...

    ParameterRequestUtils.java

    继承HttpServletRequestWrapper实现类,重新相关方法,实现自定义增加请求参数,通常是由于需要对参数进行特殊业务处理,然而HttpServletRequest的request.getPrameterMap是不允许修改的(被锁,可查看底层源码),...

    防范XSS攻击程序

    自定义一个Filter拦截器,使用 Filter来过滤浏览器发出的请求,检测每个请求的参数是否含有XSS攻击关键字,如果存在xss攻击关键字,转义特殊字符。 方法是实现一个自定义的 HttpServletRequestWrapper ,然后在 ...

    通用分页组件

    2)同时也不提供页面如何显示上页,下页等;内部提供了源代码实现,以及DEMO应用,实现分页非常简单; 需要做的工作有三个地方 1.继承实现分页的抽象类AbstractPage 的两个抽象方法; 2.调用...

    XssHttpServletRequestWrapper.java

    XssHttpServletRequestWrapper.java

    详解JSP 内置对象request常见用法

    request 对象是 HttpServletRequestWrapper 类的实例。它的继承体系如下: _request 对象继承层次结构图.png ServletRequest 接口的唯一子接口是 HttpServletRequest ,HttpServletRequest 接口的唯一实现类 ...

    MyEclipse中文字过滤器

    class Request extends HttpServletRequestWrapper { public Request(HttpServletRequest request) { super(request); } public String toChi(String input) { try { byte[] bytes=...

    使用Filter针对Xss攻击,sql注入,服务器访问白名单,以及csrf进行安全校验

    5.4,public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper 6,服务器白名单校验的java文件: 6.1,public class ServerWhiteListUtil 7,如果不要需要使用服务器白名单功能,那么,注释...

    主要使用Filter针对Xss攻击,sql注入,服务器访问白名单,以及csrf进行安全校验

    5.4,public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper 6,服务器白名单校验的java文件: 6.1,public class ServerWhiteListUtil 7,如果不要需要使用服务器白名单功能,那么,注释...

Global site tag (gtag.js) - Google Analytics