SpringBoot 之 SpringMVC拦截器从Request中获取参数并解决request的请求流只能读取一次的问题

符号 阅读:791 2021-03-31 17:00:53 评论:0

为什么使用RequestBody只能读取一遍请求数据流?

         那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。

解决办法:重写HttpServletRequestWrapper方法

        这种方法就是通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request在填充进去,这样就可以多次读取request了

 

功能代码:

package com.digipower.erms.request.wrapper; 
 
import java.io.ByteArrayInputStream; 
import java.io.IOException; 
import java.nio.charset.Charset; 
 
import javax.servlet.ReadListener; 
import javax.servlet.ServletInputStream; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletRequestWrapper; 
import org.apache.commons.io.IOUtils; 
import org.apache.commons.lang.StringEscapeUtils; 
 
public class SQLInjectionHttpServletRequestWrapper extends HttpServletRequestWrapper { 
 
	private final byte[] bytes; 
 
	public SQLInjectionHttpServletRequestWrapper(HttpServletRequest request) throws IOException { 
		super(request); 
 
		// 读取输入流里的请求参数,并保存到bytes里 
		bytes = IOUtils.toByteArray(request.getInputStream()); 
	} 
 
	public String getRequestBodyParame() { 
		return new String(bytes, Charset.forName("utf8")); 
	} 
 
	/** 
	 *  
	 * <p> 
	 * Title: getInputStream 
	 * </p> 
	 * <p> 
	 * Description:处理POST请求参数 RequestBody is missing 问题 
	 * </p> 
	 *  
	 * @return 
	 * @throws IOException 
	 * @see javax.servlet.ServletRequestWrapper#getInputStream() 
	 */ 
	@Override 
	public ServletInputStream getInputStream() throws IOException { 
		String body = new String(this.bytes); 
		return new BufferedServletInputStream(cleanXSS(body).getBytes()); 
	} 
 
	class BufferedServletInputStream extends ServletInputStream { 
		private ByteArrayInputStream inputStream; 
 
		public BufferedServletInputStream(byte[] buffer) { 
			// 此处即赋能,可以详细查看ByteArrayInputStream的该构造函数; 
			this.inputStream = new ByteArrayInputStream(buffer); 
		} 
 
		@Override 
		public int available() throws IOException { 
			return inputStream.available(); 
		} 
 
		@Override 
		public int read() throws IOException { 
			return inputStream.read(); 
		} 
 
		@Override 
		public int read(byte[] b, int off, int len) throws IOException { 
			return inputStream.read(b, off, len); 
		} 
 
		@Override 
		public boolean isFinished() { 
			// TODO Auto-generated method stub 
			return false; 
		} 
 
		@Override 
		public boolean isReady() { 
			// TODO Auto-generated method stub 
			return false; 
		} 
 
		@Override 
		public void setReadListener(ReadListener listener) { 
			// TODO Auto-generated method stub 
 
		} 
	} 
 
	/** 
	 *  
	 * <p> 
	 * Title: getParameterValues 
	 * </p> 
	 * <p> 
	 * Description: 解决html 和javascript 脚本注入问题 
	 * </p> 
	 *  
	 * @param name 
	 * @return 
	 * @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String) 
	 */ 
	@Override 
	public String[] getParameterValues(String parameter) { 
		// TODO Auto-generated method stub 
		String[] values = super.getParameterValues(parameter); 
		if (values == null) { 
			return null; 
		} 
		int count = values.length; 
		String[] encodedValues = new String[count]; 
		for (int i = 0; i < count; i++) { 
			encodedValues[i] = cleanXSS(values[i]); 
		} 
		return encodedValues; 
	} 
 
	@Override 
	public String getParameter(String parameter) { 
		String value = super.getParameter(parameter); 
		return value; 
	} 
 
	@Override 
	public String getHeader(String name) { 
		String value = super.getHeader(name); 
		return value; 
	} 
 
} 

2、过滤器SQLInjectionFilter

package com.digipower.erms.filter; 
 
import java.io.IOException; 
 
import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.annotation.WebFilter; 
import javax.servlet.http.HttpServletRequest; 
 
import com.digipower.erms.request.wrapper.SQLInjectionHttpServletRequestWrapper; 
/** 
 *  
 * @ClassName:  SQLInjectionFilter    
 * @Description: ServletRequest 读取RequestBody is miss的问题   
 * @date:   2018年11月7日 上午9:03:25    
 *      
 */ 
@WebFilter(filterName="SQLInjectionFilter",urlPatterns="/*") 
public class SQLInjectionFilter implements Filter { 
 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
        // TODO Auto-generated method stub 
    } 
 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException { 
        // TODO Auto-generated method stub 
         if(request instanceof HttpServletRequest) { 
             request = new SQLInjectionHttpServletRequestWrapper((HttpServletRequest) request); 
         } 
         chain.doFilter(request, response); 
    } 
 
    @Override 
    public void destroy() { 
        // TODO Auto-generated method stub 
 
    } 
 
} 

springboot 配置Filter

package com.digipower; 
  
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.boot.builder.SpringApplicationBuilder; 
import org.springframework.boot.web.servlet.ServletComponentScan; 
import org.springframework.boot.web.support.SpringBootServletInitializer; 
  
@SpringBootApplication 
@ServletComponentScan("com.digipowert.erms.filter") 
public class Application extends SpringBootServletInitializer { 
  
	public static void main(String[] args) { 
		// TODO Auto-generated method stub 
		 SpringApplication.run(Application.class, args); 
	} 
	 
	@Override 
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 
        // 注意这里要指向原先用main方法执行的Application启动类 
        return builder.sources(Application.class); 
    } 
}

现在我们已经完成HttpServletRequest  转换为自定义SQLInjectionHttpServletRequestWrapper  包装对象。

下面说一下其应用场景:SQL 攻击注入、SpringAOP 参数日志记录。

SQL 攻击注入:SpringBoot 之 SpringMVC 实现SQL注入过滤 完整版

SpringAOP 参数日志记录:SpringBoot 之SpringAOP 请求日志记录功能(支持POST和GET)

标签:Spring Boot
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号