怎么在Spring中使用MVC接口防止數(shù)據(jù)重復(fù)提交?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
創(chuàng)新互聯(lián)建站專(zhuān)注于企業(yè)成都全網(wǎng)營(yíng)銷(xiāo)、網(wǎng)站重做改版、卓資網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、成都h5網(wǎng)站建設(shè)、電子商務(wù)商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性?xún)r(jià)比高,為卓資等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
一、自定義一個(gè)注解,此注解可以使用在方法上或類(lèi)上
使用在方法上,表示此方法需要數(shù)據(jù)校驗(yàn)
使用在類(lèi)上,表示此類(lèi)下的所有方法需要數(shù)據(jù)校驗(yàn)
此注解對(duì)無(wú)參數(shù)方法不起作用
import org.springframework.stereotype.Component; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface DataValidate { }
二、自定義攔截,攔截前端所有請(qǐng)求
1、檢查此接口調(diào)用的方法或方法所在的類(lèi)是否使用了DataValidate注解,若沒(méi)有使用,表示此接口不需要校驗(yàn)數(shù)據(jù);
2、若使用了注解,再檢查此方法有沒(méi)有入?yún)?,若沒(méi)有入?yún)?,不需要校?yàn)數(shù)據(jù),否在需要校驗(yàn);
3、把前端傳來(lái)的所有參數(shù) (除了簽名參數(shù))按照參數(shù)升序生成一個(gè)json字符串(使用TreeMap方式自動(dòng)排序);
4、把生成的json字符串通過(guò)MD5加密的結(jié)果和前端傳的簽名值對(duì)比,若不相等,表示此數(shù)據(jù)被篡改過(guò),否在沒(méi)有被篡改過(guò);
5、數(shù)據(jù)是否被篡改校驗(yàn)完畢,若前端傳了用戶(hù)唯一標(biāo)示(token),表示需要校驗(yàn)數(shù)據(jù)是否重復(fù)提交;
6、若簽名和上次提交的數(shù)據(jù)的簽名相等,表示是重復(fù)提交數(shù)據(jù),若不相等,把簽名保存下來(lái),表示數(shù)據(jù)不是重復(fù)提交。
import java.security.MessageDigest; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.PreDestroy; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.MethodParameter; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.alibaba.fastjson.JSON; /** * 防數(shù)據(jù)被篡改和重復(fù)提交 */ public class DataValidateInterceptor extends HandlerInterceptorAdapter implements Runnable { public static Map<String, TokenValue> userToken = new ConcurrentHashMap<>(); // 過(guò)期時(shí)間 private static long EXPIRED_TIME = 3600000; private static String TOKEN_NAME = "token"; private static String SIGN_NAME = "sign"; private volatile boolean shutDown; public DataValidateInterceptor(@Value("${data_interceptor.expired_time}") String expiredTime, @Value("${data_interceptor.token_name}") String tokenName, @Value("${data_interceptor.sign_name}") String signName) { if (null != expiredTime && !"".equals(expiredTime)) { EXPIRED_TIME = Long.parseLong(expiredTime); } if (null != tokenName && !"".equals(tokenName)) { TOKEN_NAME = tokenName; } if (null != signName && !"".equals(signName)) { SIGN_NAME = signName; } } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (validate(request, response, handler)) { /** * 實(shí)現(xiàn)返回提示數(shù)據(jù) */ response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.getWriter().write("參數(shù)驗(yàn)證失敗!"); return false; } return true; } private boolean validate(HttpServletRequest request, HttpServletResponse response, Object handler) { if (handler instanceof HandlerMethod) { Class<?> clazz = ((HandlerMethod) handler).getBeanType(); DataValidate dataValidate = clazz.getAnnotation(DataValidate.class); if (null == dataValidate) { dataValidate = ((HandlerMethod) handler).getMethodAnnotation(DataValidate.class); } if (dataValidate != null) { MethodParameter[] methodParameters = ((HandlerMethod) handler).getMethodParameters(); if (null == methodParameters || methodParameters.length <=0) { // 方法沒(méi)有入?yún)⒉恍枰r?yàn) return false; } // 需要校驗(yàn) String sign = request.getParameter(SIGN_NAME); Map<String, String[]> params = request.getParameterMap(); Set<String> paramNames = params.keySet(); Map<String, String> paramsMap = new TreeMap<>(); for (String paramName : paramNames) { if (paramName.equals(SIGN_NAME)) { continue; } paramsMap.put(paramName, request.getParameter(paramName)); } String paramString = JSON.toJSONString(paramsMap).replaceAll(" ", ""); String MD5Sign = MD5.getMD5(paramString); if (!sign.equals(MD5Sign)) { // 數(shù)據(jù)被篡改 return true; } String token = request.getParameter(TOKEN_NAME); if (token != null) { if (userToken.containsKey(token)) { TokenValue tokenValue = userToken.get(token); if (tokenValue.getValue().equals(sign)) { // 數(shù)據(jù)已經(jīng)提交過(guò) return true; } else { tokenValue.setValue(sign); } } else { userToken.put(token, new TokenValue(sign)); } } } } return false; } @Override public void run() { try { while (!shutDown) { synchronized (this) { wait(EXPIRED_TIME); Set<String> keys = userToken.keySet(); for (String key : keys) { if ((userToken.get(key).getExpiredTime() + EXPIRED_TIME) <= System.currentTimeMillis()) { userToken.remove(key); } } } } } catch (Exception e) { e.printStackTrace(); } } @PreDestroy public void custDestroy() { shutDown = true; synchronized (this) { notifyAll(); } } private static class MD5 { /** * 向getMD5方法傳入一個(gè)你需要轉(zhuǎn)換的原始字符串,將返回字符串的MD5碼 * * @param code 原始字符串 * @return 返回字符串的MD5碼 */ private static String getMD5(String code) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); byte[] bytes = code.getBytes(); byte[] results = messageDigest.digest(bytes); StringBuilder stringBuilder = new StringBuilder(); for (byte result : results) { // 將byte數(shù)組轉(zhuǎn)化為16進(jìn)制字符存入stringbuilder中 stringBuilder.append(String.format("%02x", result)); } return stringBuilder.toString(); } catch (Exception e) { e.printStackTrace(); return ""; } } } } public class TokenValue { private String value; private long expiredTime; public TokenValue(String value) { this.value = value; this.expiredTime = System.currentTimeMillis(); } public String getValue() { return value; } public void setValue(String value) { this.value = value; this.expiredTime = System.currentTimeMillis(); } public long getExpiredTime() { return expiredTime; } }
三、使用
后端使用:
1.在需要數(shù)據(jù)校驗(yàn)的方法或類(lèi)上使用DataValidate注解(若在類(lèi)上使用,表示此類(lèi)下的所有方法需要驗(yàn)證)
2.配置 DataValidateInterceptor 攔截器
3.配置前端簽名參數(shù),默認(rèn)是sign
4.配置用戶(hù)唯一標(biāo)示參數(shù),默認(rèn)是token(防止數(shù)據(jù)重復(fù)提交需要)
5.配置用戶(hù)唯一標(biāo)示過(guò)期時(shí)間,默認(rèn)是1h,單位是ms(防止數(shù)據(jù)重復(fù)提交需要)
前端使用:
1.獲取用戶(hù)唯一標(biāo)示(防止數(shù)據(jù)重復(fù)提交需要)
2.把需要提交的數(shù)據(jù)根據(jù)參數(shù)(包括用戶(hù)唯一標(biāo)示)升序排序然后生成一個(gè)json字符串(不能有空格),最后把json字符串進(jìn)行MD5加密生成32位小寫(xiě)加密結(jié)果簽名
eg:需要提交的數(shù)據(jù)為{messageType: "userQueryWait", companyCode: "test_app", token:"123213213"},排序后生成json字符串 {"companyCode":"test_app","messageType":"userQueryWait","token":"123213213"}, md5生成簽名
3.把簽名和需要提交的數(shù)據(jù)一起傳到后臺(tái)
eg:{messageType: "userQueryWait", companyCode: "test_app", token:"123213213", sign:"719bdb1fb769efb68e40440d1628ed5b"}
看完上述內(nèi)容,你們掌握怎么在Spring中使用MVC接口防止數(shù)據(jù)重復(fù)提交的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
本文名稱(chēng):怎么在Spring中使用MVC接口防止數(shù)據(jù)重復(fù)提交
當(dāng)前地址:http://aaarwkj.com/article40/ijpjho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、靜態(tài)網(wǎng)站、網(wǎng)站導(dǎo)航、用戶(hù)體驗(yàn)、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)