SpringMVC接口怎么防数据篡改和重复提交,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
站在用户的角度思考问题,与客户深入沟通,找到余杭网站设计与余杭网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:成都网站制作、网站设计、企业官网、英文网站、手机端网站、网站推广、申请域名、虚拟主机、企业邮箱。业务覆盖余杭地区。
一、自定义一个注解,此注解可以使用在方法上或类上
使用在方法上,表示此方法需要数据校验 使用在类上,表示此类下的所有方法需要数据校验 此注解对无参数方法不起作用
import org.springframework.stereotype.Component; @Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface DataValidate { }
二、自定义拦截,拦截前端所有请求
1、检查此接口调用的方法或方法所在的类是否使用了DataValidate注解,若没有使用,表示此接口不需要校验数据;2、若使用了注解,再检查此方法有没有入参,若没有入参,不需要校验数据,否在需要校验;3、把前端传来的所有参数 (除了签名参数)按照参数升序生成一个json字符串(使用TreeMap方式自动排序);4、把生成的json字符串通过MD5加密的结果和前端传的签名值对比,若不相等,表示此数据被篡改过,否在没有被篡改过;5、数据是否被篡改校验完毕,若前端传了用户唯一标示(token),表示需要校验数据是否重复提交;6、若签名和上次提交的数据的签名相等,表示是重复提交数据,若不相等,把签名保存下来,表示数据不是重复提交。
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; /** * 防数据被篡改和重复提交 */ public class DataValidateInterceptor extends HandlerInterceptorAdapter implements Runnable { public static Map userToken = new ConcurrentHashMap<>(); // 过期时间 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)) { /** * 实现返回提示数据 */ response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.getWriter().write("参数验证失败!"); 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) { // 方法没有入参不需要校验 return false; } // 需要校验 String sign = request.getParameter(SIGN_NAME); Map params = request.getParameterMap(); Set paramNames = params.keySet(); Map 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)) { // 数据被篡改 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)) { // 数据已经提交过 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 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方法传入一个你需要转换的原始字符串,将返回字符串的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数组转化为16进制字符存入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.在需要数据校验的方法或类上使用DataValidate注解(若在类上使用,表示此类下的所有方法需要验证)
2.配置 DataValidateInterceptor 拦截器
3.配置前端签名参数,默认是sign
4.配置用户唯一标示参数,默认是token(防止数据重复提交需要)
5.配置用户唯一标示过期时间,默认是1h,单位是ms(防止数据重复提交需要)
前端使用:
1.获取用户唯一标示(防止数据重复提交需要)
2.把需要提交的数据根据参数(包括用户唯一标示)升序排序然后生成一个json字符串(不能有空格),最后把json字符串进行MD5加密生成32位小写加密结果签名
eg:需要提交的数据为{messageType: "userQueryWait", companyCode: "test_app", token:"123213213"},排序后生成json字符串 {"companyCode":"test_app","messageType":"userQueryWait","token":"123213213"}, md5生成签名
3.把签名和需要提交的数据一起传到后台
eg:{messageType: "userQueryWait", companyCode: "test_app", token:"123213213", sign:"719bdb1fb769efb68e40440d1628ed5b"}
看完上述内容,你们掌握SpringMVC接口怎么防数据篡改和重复提交的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!
文章标题:SpringMVC接口怎么防数据篡改和重复提交
本文网址:
http://cdiso.cn/article/pcjpie.html