问题描述:
在开发的过程中,为了保证请求数据和返回数据的安全性,通常需要进行请求和返回数据加密,但是请求数据通常是密文,会导致Controller层无法获取请求对象。
思路:
使用过滤器或aop统一对请求参数进行拦截,从HttpServletRequest获取加密数据后,进行解密,把解密后的json字符串重新填回HttpServletRequest就,Controller层就可以接收到解密后的json对象了。
httpServletRequest只有getInputStream方法可以获取输入流且只能获取一次,但是没有setRequestBody的功能,
以zuulFilter为例的微服务工程,解决方法如下:
定义一个类(以下示例类名为BodyReaderHttpServletRequestWrapper )继承HttpServletRequestWrapper,在自定义的继承了zuulFilter的类中获取HttpServletRequest,构造一个BodyReaderHttpServletRequestWrapper ,将HttpServletRequest对象传入,setBody将解密后字符串传入然后调用getInputStream(),就可以完成HttpServletRequest中RequestBody的重写;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{
private String getRequestBody(InputStream stream) {
String line = "";
StringBuilder body = new StringBuilder();
int counter = 0;
// 读取POST提交的数据内容
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
try {
while ((line = reader.readLine()) != null) {
if (counter > 0) {
body.append("rn");
}
body.append(line);
counter++;
}
} catch (IOException e) {
e.printStackTrace();
}
return body.toString();
}
private HashMap<String, String[]> getParamMapFromPost(HttpServletRequest request) {
String body = "";
try {
body = getRequestBody(request.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
HashMap<String, String[]> result = new HashMap<String, String[]>();
if (null == body || 0 == body.length()) {
return result;
}
return parseQueryString(body);
}
public HashMap<String, String[]> parseQueryString(String s) {
String valArray[] = null;
if (s == null) {
throw new IllegalArgumentException();
}
HashMap<String, String[]> ht = new HashMap<String, String[]>();
StringTokenizer st = new StringTokenizer(s, "&");
while (st.hasMoreTokens()) {
String pair = (String) st.nextToken();
int pos = pair.indexOf('=');
if (pos == -1) {
continue;
}
String key = pair.substring(0, pos);
String val = pair.substring(pos + 1, pair.length());
if (ht.containsKey(key)) {
String oldVals[] = (String[]) ht.get(key);
valArray = new String[oldVals.length + 1];
for (int i = 0; i < oldVals.length; i++) {
valArray[i] = oldVals[i];
}
} else {
valArray = new String[1];
}
ht.put(key, valArray);
}
return ht;
}
private Map<String, String[]> getParamMapFromGet(HttpServletRequest request) {
return parseQueryString(request.getQueryString());
}
// 报文
private byte[] body;
public String getBody(){
return new String(body);
}
public void setBody(String bodypa){
body = bodypa.getBytes();
}
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = readBytes(request.getInputStream());
//
/*if ("POST".equals(request.getMethod().toUpperCase())) {
paramsMap = getParamMapFromPost(this);
} else {
paramsMap = getParamMapFromGet(this);
}*/
}
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request, byte[] encodeBody) throws IOException {
super(request);
body = encodeBody;
//
/*if ("POST".equals(request.getMethod().toUpperCase())) {
paramsMap = getParamMapFromPost(this);
} else {
paramsMap = getParamMapFromGet(this);
}*/
}
@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 boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener arg0) {
}
};
}
private static byte[] readBytes(InputStream in) throws IOException {
BufferedInputStream bufin = new BufferedInputStream(in);
int buffSize = 1024;
ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);
byte[] temp = new byte[buffSize];
int size = 0;
while ((size = bufin.read(temp)) != -1) {
out.write(temp, 0, size);
}
bufin.close();
byte[] content = out.toByteArray();
return content;
}
}
拦截器或aop中使用:
//获取HttpServletRequest
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
//获取RequestBody
ServletInputStream inputStream = request.getInputStream();
if (inputStream != null) {
secParamStr = IOUtils.toString(inputStream);
log.info("入参 = {}", secParamStr);
//此处进行解密,获取解密后的字符串
String decodeJson = SM4util.decode(secParamStr);
}
log.info("解密结果 = {}", decodeJson );
//requestBody明文回填
BodyReaderHttpServletRequestWrapper brtsr = new BodyReaderHttpServletRequestWrapper(request);
brtsr.setBody(decodeJson );
brtsr.getInputStream();
上述方法如果不行,可以使用更简单点的:
/**
* 重新post请求参数
* @param ctx RequestContext 对象
* @param paramBytes 需要重写进post请求体的jsonStr
*/
public static void rewriteRequest(RequestContext ctx, byte[] paramBytes){
ctx.setRequest(new HttpServletRequestWrapper(ctx.getRequest()){
@Override
public ServletInputStream getInputStream() throws IOException{
return new ServletInputStreamWrapper(paramBytes);
}
@Override
public int getContentLength(){
return paramBytes.length;
}
@Override
public long getContentLengthLong(){
return paramBytes.length;
}
});
}
//---在继承了zuulFilter的类中的run方法使用:这里使用SM4做例子,加解密工具类暂不提供
RequestContext ctx = RequestContext.getCurrentContext();
ServletInputStream inputStream = ctx.getRequest().getInputStream();
String encodeStr = IOUtils.toString(inputStream);
String decodeParams = SM4Utils.decodeECB(encodeStr);
rewriteRequest(ctx,decodeParams.getBytes(Charset.defaultCharset()));