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

为什么使用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)

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值