前端传来的参数是json格式的数据,并非传统的表单提交,因为服务端使用spring mvc框架,首先想到了Spring mvc 自带的@RequestBody注解,直接将传递参数注入处处理方法的参数中,
可是这样遇到了一个问题,在使用拦截器对客户端传递参数进行校验时是无法直接经过request.getParameter("name")获取该对象的参数,只能经过request.getInputStream();将传递对象的body读出,解析json字符串,对参数进行处理,
拦截器的方法跑通了,到了具体的处理方法时,注入参数报错了前端
缘由是,以前的在拦截器中的处理方法已经经过request.getInputStream()取出了输入流,参数解析时已经拿不到了,此时想到两种解决方法:java
1.将全部的参数解析及参数校验逻辑写在每一个方法中git
2.让request.getInputStream()再次取值时还能取到web
第一种方法,可以很快实现,可是会产生大量的重复代码spring
因而决定使用第二种方法,代码以下json
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对象进行包装mvc
@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()); } }
参数校验拦截器app
在web.xml中配置时,将该过滤器放在最后,实践中使用了shiro框架,放在前边时形成了错误框架