版权声明:本文为博主原创文章,欢迎分享转载。 https://blog.csdn.net/qq_29897369/article/details/84669428
-
业务场景介绍
在我们开发中不管是web 还是给别人的api 中一旦涉及到事务操作 。 比如添加、修改等等。一旦重复提交后造成数据错误。后果可想而知。目前常用的解决方案有大致两个方向:web端防止重复提交和服务端防止重复提交。具体方案有按钮不可点击、弹框、服务端token。今天要记录就是通过注解方式实现token 从而实现防止表单重复提交。
-
思路介绍
首先在调用我们需要加防止重复提交的方法前,调用我们的生成token 接口,接口返回生成唯一token ,比如时间戳或是UUID都可以。在服务端保存比如Redis。web 端也保存该token。在执行的时候带上该参数在请求头。通过注解和redis 保存的比较。redis 以set形式保存同时有有效期。在有效期内比较是否存在。同时比较加上同步关键字,防止并发。存在的话删除redis中的token。放行执行后面的方法。 -
如何自定义防止重复提交的注解
1、定义注解
package com.api.annotation;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
*
* @ClassName: ValidateRepeatableRequest
* @Description:TODO(防止重复提交注解)
* @author: drj
* @date: 2018年11月29日 下午11:14:09
*
* @Copyright: 2018
*
*/
@Documented
@Retention(RUNTIME)
@Target({ TYPE, METHOD })///接口、类、枚举、注解、方法
public @interface ValidateRepeatableRequest {
}
2、实现注解类
package com.api.aspect;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.api.util.HttpContextUtils;
/**
*
* @ClassName: ValidateRepeatableRequestAspect
* @Description:TODO(注解类)
* @author: drj
* @date: 2018年11月30日 下午10:20:01
*
* @Copyright: 2018
*
*/
@Component
@Aspect
public class ValidateRepeatableRequestAspect {
private static final Logger logger = LoggerFactory.getLogger(ValidateRepeatableRequestAspect.class);
@Autowired
private RedisUtil jedis;
private String redisHashKey = "token";
@Pointcut("@annotation(com.api.annotation.ValidateRepeatableRequest)")
public void validateRepeatableRequest() {
}
/**
* 方法前执行
* @param joinPoint
* @throws Throwable
*/
@Before("validateRepeatableRequest()")
public void before(JoinPoint joinPoint) throws Throwable {
// 获取http请求对象
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
String busikey = request.getParameter("token");
if (StringUtils.isBlank(busikey)) {
JSONObject json = getParams(joinPoint);
busikey = (String) json.get("token");
}
if (StringUtils.isBlank(busikey)) {
throw new Exception("当前请求不合法,请刷新后重试!");
}
synchronized (busikey) {
Long delCount = jedis.hdel(redisHashKey, busikey);
if (delCount == null || delCount != 1) {
logger.info("接口重复提交,或者页面长时间不操作导致token失效!");
throw new RuntimeException("当前页面数据已更新,请刷新后重试!");
}
}
}
/**
* 获取json参数
*
* @param joinPoint
* @return
*/
private JSONObject getParams(JoinPoint joinPoint) {
// 获取参数值
Object[] args = joinPoint.getArgs();
if (args == null) {
return null;
}
JSONObject params = new JSONObject();
// 对象接收参数
try {
String data = JSON.toJSONString(joinPoint.getArgs()[0]);
params = JSON.parseObject(data);
}
// 普通参数传入
catch (JSONException e) {
// 获取参数名
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
for (int i = 0; i < methodSignature.getParameterNames().length; i++) {
params.put(methodSignature.getParameterNames()[i], args[i]);
}
}
return params;
}
}
package com.api.util;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
*
* @ClassName: HttpContextUtils
* @Description:TODO(http工具类)
* @author: drj
* @date: 2018年11月29日 下午11:45:06
*
* @Copyright: 2018
*
*/
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain(){
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin(){
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
}
- 如何使用自定义注解
@RequestMapping("/getMyToken")
@ResponseBody
@ValidateRepeatableRequest
public String getMyToken(HttpServletRequest request, HttpServletResponse response) {
String token = jedis.get("token");
if (token != null && !token.equals("")) {
return token;
} else {
jedis.setex("token", 1 * 60 * 10, ToolsUtil.GetGUID());
return jedis.get("token");
}
}