Use Spring's AOP aspect to prevent repeated form submissions
scenes to be used
When submitting the form at the front end, due to network lag or misoperation, the user clicks the submit button multiple times, resulting in multiple new duplicate data.
The backend can use the AOP aspect to prevent repeated submissions. When the first submission is not processed, if the same data is submitted again, it will not be processed.
This example is relatively simple and can only prevent repeated submission of the same data at the same time; if you need to prevent the submission of different data of the same form, you need to modify the front-end code and carry a one-time form credential when submitting the form.
example code
Tools
HashUtils
Tool class, used to calculate the value of the stringMD5
, the code is as follows:
import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;/** * @author hyx */public class HashUtils { /** * Get the md5 value of a string */ public static String hashKeyForDisk(String key) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(key.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(key.hashCode()); } return cacheKey; } private static String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); }}
RedisLockService
Used to operateredis
locking and unlocking, the code is as follows:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.data.redis.core.script.RedisScript;import org.springframework.data.redis.serializer.StringRedisSerializer;import org.springframework.stereotype.Service;import java.util.Collections;/** * @author hyx */@Servicepublic class RedisLockService { private static final Long SUCCESS = 1L; @Autowired private RedisTemplate redisTemplate; /** * Get the lock * * @param lockKey * @param value * @param expireTime lock valid time unit-second * @return */ public boolean getLock(String lockKey, String value, int expireTime) { boolean ret = false; try { String script = "if redis.call('setNx',KEYS[1],ARGV[1]) == 1 then if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end else return 0 end"; RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class); Object result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(), Collections.singletonList(lockKey), value, String.valueOf(expireTime)); if (SUCCESS.equals(result)) { return true; } } catch (Exception e) { e.printStackTrace(); } return ret; } /** * release lock * * @param lockKey * @param value * @return */ public boolean releaseLock(String lockKey, String value) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class); Object result = redisTemplate.execute(redisScript, new StringRedisSerializer(), new StringRedisSerializer(), Collections.singletonList(lockKey), value); if (SUCCESS.equals(result)) { return true; } return false; }}
Sliced
Create an
ASubmit
annotation to be used as the entry point of the aspect, the code is as follows:
import java.lang.annotation.*; /** * @author hyx */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ASubmit { }
The aspect class implements anti-duplication submission, the code is as follows:
import com.alibaba.fastjson.JSONObject; import com.transnal.business.api.ApiException; import com.transnal.business.api.ReplyCode; import com.transnal.common.spring.ApplicationContextHolder; import com.transnal.publish.common.utils.HashUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * @author hyx */ @Aspect @Component public class SubmitAspect { //annotation entry point @Pointcut("@annotation(com.transnal.publish.admin.ASubmit)") //method entry point // @Pointcut("execution(public * com.transnal.publish.admin..*Controller.create(..))") public void addAdvice() { } /** * Surround notifications */ @Around("addAdvice()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { RedisLockService redisLockService = ApplicationContextHolder.getBean(RedisLockService.class); Object[] args = joinPoint.getArgs(); //Calculate the md5 of the request parameter String md5Key = HashUtils.hashKeyForDisk(JSONObject.toJSONString(args)); //Use the md5 of the parameter to lock before the method is executed. If md5 is repeated, it means repeated submission boolean lock = redisLockService.getLock(md5Key, md5Key, 30); if (lock) { try { // execute method Object proceed = joinPoint.proceed(); return proceed; } catch (Exception e) { e.printStackTrace(); throw new ApiException(ReplyCode.Error, e.getMessage()); } finally { //Unlock if the execution succeeds or fails redisLockService.releaseLock(md5Key, md5Key); } } //Failure to lock means repeated submission throw new ApiException(ReplyCode.Error, "Please do not submit again"); } }
ASubmit
You can use annotations on methods that need to prevent repeated submissions. The code is as follows:
public class SlideController { /** * new interface */ @ASubmit @Override public String create(Slide slide) { //... }}
0 Comments