为了保证接口幂等性,需要前后端都要做处理。
1 前端-提交按钮置灰,不再请求接口。
2 后盾-防重复提交。
package com.jiutong.zqp.manage.interceptor; import groovy.util.logging.Slf4j; import org.apache.commons.lang.StringUtils; import org.apache.shiro.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * bi 防重复提交 * @author zhouq */ @Slf4j public class RedisResubmitInterceptor implements HandlerInterceptor { private static final String RESUBMIT_TOKEN; static { RESUBMIT_TOKEN = "bi_resubmit_token_"; } protected Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RedisTemplate redisTemplate; private String[] NOT_INTERCEPT_URL = {"newOrders/orderStatusCount", "newOrders/getBuyerOrders","/views","/resources"}; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object shrioUser = null; try { //获取登入用户ID shrioUser = SecurityUtils.getSubject().getSession().getAttribute("adminUid"); } catch (Exception e) { } if (shrioUser == null) { return true; } Integer userId = (Integer)shrioUser; if (userId == null || Objects.equals(userId, 0)) { return true; } //获取请求路径 String methodFullName = request.getRequestURI() + "_" + userId; //其他请求不做防重复提交 for (String url: NOT_INTERCEPT_URL) { if (StringUtils.indexOf(methodFullName, url) > 0) { return true; } } //路径加密 String resubmitTokenKey = RESUBMIT_TOKEN + MD5.MD5Encode(methodFullName); logger.debug("resubmitTokenKey lock key: " + resubmitTokenKey); //redis模版k-v 键值操作 ValueOperations<String, String> valueOps = redisTemplate.opsForValue(); //redis 如果resubmitTokenKey相同,自增 long count = valueOps.increment(resubmitTokenKey, 1); // 如果等于1,说明是第一个请求,如果该KEY的数值大于1,说明是第一次请求处理未完成,重复提交的请求,不做处理 if (count == 1) { // 设置有效期 redisTemplate.expire(resubmitTokenKey, 30, TimeUnit.SECONDS); logger.debug("resubmitTokenKey get lock, key: " + resubmitTokenKey + " , expire in 20 seconds."); return true; } else { //判断日志级别 if (logger.isDebugEnabled()) { String desc = String.valueOf(valueOps.get(resubmitTokenKey)); logger.debug("resubmitTokenKey key: " + resubmitTokenKey + " locked by another business:" + desc); } return false; } } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { Object shrioUser = null; try { shrioUser = SecurityUtils.getSubject().getSession().getAttribute("adminUid"); } catch (Exception e) { } if (shrioUser == null) { return ; } Integer userId = (Integer)shrioUser; String methodFullName = request.getRequestURI() + "_" + userId; String resubmitTokenKey = RESUBMIT_TOKEN + MD5.MD5Encode(methodFullName); //响应成功,删除缓存 redisTemplate.delete(resubmitTokenKey); } }