Implementation principle of preventing repeated requests and submitting forms in Java background

Time:2021-5-8

This article mainly introduces the implementation principle of Java background to prevent the client from repeating requests and submitting forms. The example code is introduced in great detail, which has a certain reference learning value for everyone’s study or work. If you need a friend, you can refer to it

preface

In the web / APP project, there are some requests or operations that will affect the data (such as adding, deleting and modifying). Generally, some protection should be done for such requests to prevent the data confusion caused by users’ intentional or unintentional repeated requests.

Common solutions

1. Client

For example, set the submit button to disable after the form is submitted

2. Server

The limitation of the front-end can only solve a few problems, and it is not thorough enough. The back-end’s own anti duplication measures are indispensable and obligatory.

Here I provide a solution that I use in the project. In short, it is to determine whether the request URL and data are the same as the last time.

Method steps

1. Main logic:

Add an interceptor to all the URLs. Each request saves the URL to the session. The next request verifies whether the URL data is the same. If the URL data is the same, access will be denied.

Of course, I did some optimization on this basis, such as:

There are limitations in using session. After a large number of users, the server will not be able to support it. Here I use redis to replace it.

The token mechanism is added.

2. Implementation steps:

2.1 customize an annotation

/**
 * @Title: SameUrlData
 *@ Description: user defined annotation prevents form from being submitted repeatedly
 * @Auther: xhq
 * @Version: 1.0
 * @create 2019/3/26 10:43
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SameUrlData {

}

2.2 custom interceptor class

  • Check whether the sameurldata annotation is used for the method called by this interface. If not, it means that this interface does not need to be verified;
  • If annotations are used, get the request URL + parameter and remove the parameters that are changing all the time (such as timestamp and signature)
  • Check whether there is a token parameter (token represents the unique identity of different users) in the parameters, and there is no direct release
  • There is a token parameter. Take the token + URL as the key of redis and the URL + parameter as the value to store redis, and set the automatic destruction time
  • Visit again to verify if the request is repeated
import com.alibaba.fastjson.JSONObject;
import com.tuohang.hydra.framework.common.spring.SpringKit;
import com.tuohang.hydra.toolkit.basis.string.StringKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 *@ Title: prevent users from submitting data interceptor repeatedly
 *@ Description: store the URL and parameters accessed by the user in redis combined with the token, and verify whether the request interface is repeated each time
 * @Auther: xhq
 * @Version: 1.0
 * @create 2019/3/26 10:35
 */
@Component
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {

  private static Logger LOG = LoggerFactory.getLogger(SameUrlDataInterceptor.class);

  /**
   *Whether to block submission, fasle block, true release
   * @return
   */
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (handler instanceof HandlerMethod) {
      HandlerMethod handlerMethod = (HandlerMethod) handler;
      Method method = handlerMethod.getMethod();
      SameUrlData annotation = method.getAnnotation(SameUrlData.class);
      if (annotation != null) {
        if(repeatDataValidator(request)){
          //The request data is the same
          LOG.warn("please don't repeat submit,url:"+ request.getServletPath());
          JSONObject result = new JSONObject();
          result.put("statusCode","500");
          Result. Put ("message", "do not repeat request");
          response.setCharacterEncoding("UTF-8");
          response.setContentType("application/json; charset=utf-8");
          response.getWriter().write(result.toString());
          response.getWriter().close();
//Jump to page after blocking
//          String formRequest = request.getRequestURI();
//          request.setAttribute("myurl", formRequest);
//          request.getRequestDispatcher("/WebRoot/common/error/jsp/error_message.jsp").forward(request, response);
          return false;
        }Else {// if not repeating the same data
          return true;
        }
      }
      return true;
    } else {
      return super.preHandle(request, response, handler);
    }
  }
  /**
   *Verify whether the same URL data is submitted the same way, and return true for the same URL data
   * @param httpServletRequest
   * @return
   */
  public boolean repeatDataValidator(HttpServletRequest httpServletRequest){
    //Get request parameter map
    Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
    Iterator<Map.Entry<String, String[]>> it = parameterMap.entrySet().iterator();
    String token = "";
    Map<String, String[]> parameterMapNew = new HashMap<>();
    while(it.hasNext()){
      Map.Entry<String, String[]> entry = it.next();
      if(!entry.getKey().equals("timeStamp") && !entry.getKey().equals("sign")){
        //Remove the sign and timestamp parameters because they are changing all the time
        parameterMapNew.put(entry.getKey(), entry.getValue());
        if(entry.getKey().equals("token")) {
          token = entry.getValue()[0];
        }
      }
    }
    if (StringKit.isBlank(token)){
      //If there is no token, it will be released directly
      return false;
    }
    //Filtered request content
    String params = JSONObject.toJSONString(parameterMapNew);

    System.out.println("params==========="+params);

    String url = httpServletRequest.getRequestURI();
    Map<String,String> map = new HashMap<>();
    //Key is the interface and value is the parameter
    map.put(url, params);
    String nowUrlParams = map.toString();

    StringRedisTemplate smsRedisTemplate = SpringKit.getBean(StringRedisTemplate.class);
    String redisKey = token + url;
    String preUrlParams = smsRedisTemplate.opsForValue().get(redisKey);
    if(preUrlParams == null){
      //If the last data is null, the page has not been accessed
      //Store and set the expiration date, 2 seconds
      smsRedisTemplate.opsForValue().set(redisKey, nowUrlParams, 2, TimeUnit.SECONDS);
      return false;
    }Else {// otherwise, the page has been visited
      if(preUrlParams.equals(nowUrlParams)){
        //If the last URL + data is the same as the current URL + data, it means adding data repeatedly
        return true;
      }Else {// if the last URL + data is different from the current URL plus data, it is not a duplicate submission
        smsRedisTemplate.opsForValue().set(redisKey, nowUrlParams, 1, TimeUnit.SECONDS);
        return false;
      }
    }
  }
}

2.3 register interceptors

@Configuration
public class WebMvcConfigExt extends WebMvcConfig {

  /**
   *Preventing duplicate submission of interceptors
   */
  @Autowired
  private SameUrlDataInterceptor sameUrlDataInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    //Avoid static resources
    List<String> resourcePaths = defineResourcePaths();
    registry.addInterceptor(sameUrlDataInterceptor).addPathPatterns("/**").excludePathPatterns(resourcePaths);//  Repeat request
  }

  /**
   *Custom static resource path
   * 
   * @return
   */
  @Override
  public List<String> defineResourcePaths() {
    List<String> patterns = new ArrayList<>();
    patterns.add("/assets/**");
    patterns.add("/upload/**");
    patterns.add("/static/**");
    patterns.add("/common/**");
    patterns.add("/error");
    return patterns;
  }
}

Add @ sameurldata annotation to the corresponding method


@SameUrlData
@ResponseBody
@RequestMapping(value = "/saveOrUpdate")
public String saveOrUpdate(){
}

The above is the whole content of this article, I hope to help you learn, and I hope you can support developer more.

Recommended Today

Face mask detector based on retinanet

Author | guessCompile VKSource: analytics vidhya introduce Object detection is a very important field in computer vision, which is necessary for automatic driving, video surveillance, medical applications and many other fields. We are fighting an epidemic of unprecedented scale. Researchers around the world are trying to develop a vaccine or a treatment for cowid-19, while […]