前端傳來的參數是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框架,放在前邊時形成了錯誤框架