温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么在Spring中使用MVC接口防止数据重复提交

发布时间:2021-03-09 16:22:52 来源:亿速云 阅读:213 作者:Leah 栏目:编程语言

怎么在Spring中使用MVC接口防止数据重复提交?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

一、自定义一个注解,此注解可以使用在方法上或类上

  • 使用在方法上,表示此方法需要数据校验

  • 使用在类上,表示此类下的所有方法需要数据校验

  • 此注解对无参数方法不起作用

import org.springframework.stereotype.Component;   @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @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<String, TokenValue> 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<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)) {   // 数据被篡改   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<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方法传入一个你需要转换的原始字符串,将返回字符串的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"}

看完上述内容,你们掌握怎么在Spring中使用MVC接口防止数据重复提交的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI