最近做vue项目,后端在写sql注入过滤器的时候,遇上vue发送post请求,后端request.getparamter无法获取到传递参数变量的属性值,进过使用postman工具测试分析出
vue前端和post工具传输入数据格式不一致
1.vue前端post请求采用的是json数据格式
2.而用postman测试工具采用的是form表单方式,相当于URL带参数传递
所以导致后端request.getparamter无法获取数据
解决方法:
读取输入流来获取
post请求不是application/x-www-form-urlencoded的,全部直接返回,不作处理,即不会解析表单数据来放到requestparametermap中。所以通过request.getParameter(name)是获取不到的。只能使用最原始的方式,读取输入流来获取。
而在filter过滤器使用输入流来获取数据的时候,又发生了getInputStream has already been called for this request错误,导致过滤器过不去,登录不进去系统
报错原因:
HttpServletRequest.getReader()
HttpServletRequest.getInputStream() 不能在过滤器中读取一次二进制流(字符流),又在另外一个Servlet中读取一次,即一个InputSteam(BufferedReader)对象在被读取完成后,将无法再次被读取。
也就是从请求中获取流以后,流被filter中的这个 inputStreamToString(InputStream in) 这个方法处理后就被“消耗”了,这会导致,chain.doFilter(request, res)这个链在传递 request对象的时候,里面的请求流为空,导致责任链模式下,其他下游的链无法获取请求的body,从而导致程序无法正常运行,这也使得我们的这个filter虽然可以获取请求信息,但是它会导致整个应用程序不可用
报错解决思路如下:
将取出来的字符串,再次转换成流,然后把它放入到新request 对象中,在chain.doFiler方法中 传递新的request对象;
要实现这种思路,需要自定义一个类继承HttpServletRequestWrapper,然后重写public BufferedReader getReader()方法,public ServletInputStream getInputStream()方法;(这两个方法的重写实现逻辑如下:getInputStream()方法中将body体中的字符串转换为字节流(它实质上返回的是一个ServletInputStream 对象);然后通过getReader()调用---->getInputStream()方法;),继承实现重写逻辑以后,在sql过滤器filter(SqlInjectionFilter)中,使用自定义的HttpServletRequestWrapper(RepeatedlyReadRequestWrapper)将原始的HttpServletRequest对象进行再次封装;再通过RepeatedlyReadRequestWrapper对象去做dofilter(req,res)的req对象;
一、sql过滤代码 SqlInjectionFilter.java
package com.hitek.module.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
public class SqlInjectionFilter implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest args0, ServletResponse args1,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)args0;
HttpServletResponse res=(HttpServletResponse)args1;
//获得所有请求参数名
Enumeration params = req.getParameterNames();
String sql = "";
String url = ((HttpServletRequest) req).getRequestURL().toString().trim();
HttpSession userSession = req.getSession();
while (params.hasMoreElements()) {
//得到参数名
String name = params.nextElement().toString();
if (StringUtils.isNotBlank(name)) {
if (name.equalsIgnoreCase("method") || name.equalsIgnoreCase("fileCode")) {
continue;
}
}
//System.out.println("name===========================" + name + "--");
//得到参数对应值
String[] value = req.getParameterValues(name);
for (int i = 0; i < value.length; i++) {
sql = sql + value[i];
}
}
//vue请求重新包装一个ServletRequest
ServletRequest requestWrapper = new RepeatedlyReadRequestWrapper(req);
String body = HttpHelper.getBodyString(requestWrapper);
//如果是POST请求则需要获取 param 参数
String param = URLDecoder.decode(body,"utf-8");
if(param!=null&&!"".equals(param)||"".equals(sql)){
//System.out.println("============================SQL"+sql);
sql = param;
sql = sql.replaceAll("\\{", "");
sql = sql.replaceAll("\\}", "");
sql = sql.replaceAll("\"", "");
sql = sql.replaceAll(":", "");
sql = sql.replaceAll(",", "");
if (sqlValidate(sql)) {
throw new IOException("您发送请求中的参数中含有非法字符");
//String ip = req.getRemoteAddr();
} else {
chain.doFilter(requestWrapper,args1);
}
}else{
if (sqlValidate(sql)) {
throw new IOException("您发送请求中的参数中含有非法字符");
//String ip = req.getRemoteAddr();
} else {
chain.doFilter(args0,args1);
}
}
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
//效验
protected static boolean sqlValidate(String str) {
str = str.toLowerCase();//统一转为小写
String badStr = "and|exec|execute|insert|select|delete|update|count|drop|*|chr|mid|master|truncate|" +
"char|declare|sitename|net user|xp_cmdshell|;|or|-|+|and|exec|execute|insert|create|drop|" +
"table|from|grant|use|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|*|" +
"chr|mid|master|truncate|char|declare|or|;|-|--|+|like|#";//过滤掉的sql关键字,可以手动添加
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
if (str.indexOf(badStrs[i]) >= 0) {
if (!validateSpecialKeyword(badStrs[i],str)) {
return true;
}
}
}
return false;
}
private static boolean validateSpecialKeyword(String keyword,String str) {
if (StringUtils.isNotBlank(keyword)) {
if (StringUtils.isNotBlank(str)) {
if (!keyword.equals(";")) {
String[] arrStr = splitStr(keyword,str);
for (int i = 0; i < arrStr.length; i++) {
String s = arrStr[i];
if (s.startsWith(keyword)) {
if (s.length() == keyword.length()) {
continue;
}
System.out.println(keyword.length()+"-----"+s.charAt(keyword.length()));
if (s.charAt(keyword.length()) != ' ') {
continue;
} else {
return false;
}
}
}
} else {
String[] arrStr = splitStr(keyword,str);
for (int i = 0; i < arrStr.length; i++) {
String s = arrStr[i];
System.out.println("s.length=====>"+s.length());
if (s.startsWith(keyword)) {
if (s.length() == keyword.length()) {
continue;
}
if (s.charAt(keyword.length()) == '*') {
continue;
} else {
return false;
}
}
}
}
}
}
return true;
}
private static String[] splitStr(String keyword,String str) {
List<String> sList = new ArrayList<String>();
String s = str;
int nu = -1;
Character c = null;
while ((nu = s.indexOf(keyword)) >= 0) {
String tmpStr = "";
if (c != null) {
tmpStr = c.toString();
tmpStr += s.substring(0,nu);
} else {
tmpStr = s.substring(0,nu);
}
s = s.substring(nu);
c = s.charAt(0);
s = s.substring(1);
sList.add(tmpStr);
}
if (c != null) {
sList.add(c.toString() + s);
} else {
sList.add(s);
}
String[] arr = new String[sList.size()];
sList.toArray(arr);
return arr;
}
}
重新包装类:RepeatedlyReadRequestWrapper.java
package com.hitek.module.util;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Enumeration;
/**
* @author zhuningyi
* @desc 重复读取HttpServletRequest reader/inputstream
* @date 2023/8/9
*/
public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RepeatedlyReadRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
System.out.println("-------------------------------------------------");
Enumeration e = request.getHeaderNames() ;
while(e.hasMoreElements()){
String name = (String) e.nextElement();
String value = request.getHeader(name);
System.out.println(name+" = "+value);
}
body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
}
@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 {
return bais.read();
}
};
}
@Override
public String getHeader(String name) {
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
return super.getHeaderNames();
}
@Override
public Enumeration<String> getHeaders(String name) {
return super.getHeaders(name);
}
}
HttpHelper.java
package com.hitek.module.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.servlet.ServletRequest;
public class HttpHelper {
/**
* 获取请求Body
*
* @param request
* @return
*/
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = 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();
}
}
}
return sb.toString();
}
}