前端传来的参数是json格式的数据,并不是传统的表单提交,由于服务端使用spring mvc框架,首先想到了Spring mvc 自带的@RequestBody注解,直接将传递参数注入到处理方法的参数中,
但是这样遇到了一个问题,在使用拦截器对客户端传递参数进行校验时是没法直接通过request.getParameter("name")获取该对象的参数,只能通过request.getInputStream();将传递对象的body读出,解析json字符串,对参数进行处理,
拦截器的方法跑通了,到了具体的处理方法时,注入参数报错了
原因是,之前的在拦截器中的处理方法已经通过request.getInputStream()取出了输入流,参数解析时已经拿不到了,此时想到两种解决方法:
1.将所有的参数解析及参数校验逻辑写在每个方法中
2.让request.getInputStream()再次取值时还能取到
第一种方法,能够很快实现,但是会产生大量的重复代码
于是决定使用第二种方法,代码如下
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
public class BodyServletRequest extends HttpServletRequestWrapper {
private byte[] body;
public BodyServletRequest(HttpServletRequest request) {
super(request);
try {
body = getBodyString(request).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private 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();
}
}
使用HttpServletRequestWrapper 做一个包装类
import com.hotel.system.support.http.BodyServletRequest;
import com.hotel.system.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Slf4j
public class RequestWrapperFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getContentType() != null && request.getContentType().contains("json")) {
request = new BodyServletRequest((HttpServletRequest) request);
this.logUserRequestInfo((HttpServletRequest) request);
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
private void logUserRequestInfo(HttpServletRequest request) {
log.info("request url" + request.getRequestURI());
log.info("contentType" + request.getContentType());
log.info("http body" + HttpUtils.getContent(request));
}
}
使用RequestWrapperFilter对request对象进行包装
@Slf4j
public class NeedSignInterceptor extends BaseInterceptor {
@Value("${isDev}")
private String isDev;
private String appKey = "04c4b414a75949ad9908771e6167d07c";
private static final int TIMESTAMP_EXPIRE = 360000;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (Boolean.parseBoolean(isDev)) {
return true;
}
HandlerMethod handlerMethod = null;
if (handler instanceof HandlerMethod) {
//输出方法请求参数
handlerMethod = (HandlerMethod) handler;
logMethodInfo(handlerMethod, request);
NeedSign needAppSign = null;
needAppSign = handlerMethod.getBean().getClass().getAnnotation(NeedSign.class);
if (needAppSign == null) {
needAppSign = handlerMethod.getMethodAnnotation(NeedSign.class);
}
if (needAppSign != null && needAppSign.isNeed()) {
//签名验证逻辑
JSONObject paramsJsonObject = HttpUtils.getJsonObject(request);
Map<String, String> params = HttpUtils.getParamsMap(paramsJsonObject);
TreeMap<String, String> treeMap = new TreeMap<>(params);
log.info("interceptor params :" + treeMap.toString());
String timeStamp = treeMap.get("timestamp");
Date date = new Date(Long.parseLong(timeStamp));
if (date.getTime() - (System.currentTimeMillis()) > TIMESTAMP_EXPIRE) {
// errorResponse(response, ResponseType.TIMEOUT);
}
String sign = params.get("sign");
if (!isSignAllow(treeMap, sign)) {
errorResponse(response, ResponseType.SIGN_ERROR);
return false;
}
}
}
return true;
}
private boolean isSignAllow(TreeMap<String, String> map, String sign) {
if (StringUtils.isNotEmpty(sign)) {
if (StringUtils.isNotEmpty(appKey) && sign.trim().equalsIgnoreCase(getSign(map, appKey))) {
return true;
}
}
return false;
}
private String getSign(TreeMap<String, String> params, String appkey) {
StringBuilder sig = new StringBuilder();
// for (String value : params.values()) {
// sig.append(value);
// }
for (Map.Entry<String, String> entry : params.entrySet()) {
if (!"sign".equals(entry.getKey())) {
sig.append(entry.getValue());
}
}
sig.append(appkey);
try {
String sign = getMd5str(sig.toString().getBytes("UTF-8"));
log.info("sig string : " + sig.toString());
log.info("sign: " + sign);
return sign;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* 取字节数组的md5值的十六进制串表示
*/
public String getMd5str(byte[] b) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
return bytetoHexString(md.digest(b)).toLowerCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 将字节数组,转换为字符串
*/
private String bytetoHexString(byte[] data) {
char[] hexdigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
char[] tem = new char[data.length * 2];
for (int i = 0; i < data.length; i++) {
byte b = data[i];
tem[i * 2] = hexdigits[b >>> 4 & 0x0f];
tem[i * 2 + 1] = hexdigits[b & 0x0f];
}
return new String(tem);
}
private void logMethodInfo(HandlerMethod handlerMethod, HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
Map<String, String[]> params = request.getParameterMap();
for (Map.Entry<String, String[]> entry : params.entrySet()) {
sb.append(entry.getKey()).append(":").append(Arrays.toString(entry.getValue())).append(" ");
}
log.info(handlerMethod.getMethod().getName() + sb.toString());
}
}
参数校验拦截器
在web.xml中配置时,将该过滤器放在最后,实践中使用了shiro框架,放在前边时造成了错误
来源:oschina
链接:https://my.oschina.net/u/2405562/blog/1558942