package com.ruoyi.framework.aspectj;
import cn.hutool.crypto.SecureUtil;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.properties.TokenProperties;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 防æ¢é‡å¤æäº¤
*
* @author Lion Li
*/
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Aspect
@Component
public class RepeatSubmitAspect {
private final TokenProperties tokenProperties;
private final RepeatSubmitProperties repeatSubmitProperties;
@Before("@annotation(repeatSubmit)")
public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
// 如果注解ä¸ä¸º0 则使用注解数值
long interval = repeatSubmitProperties.getInterval();
if (repeatSubmit.interval() > 0) {
interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());
}
if (interval < 1000) {
throw new ServiceException("é‡å¤æäº¤é—´é𔿗¶é—´ä¸èƒ½å°äºŽ'1'ç§’");
}
HttpServletRequest request = ServletUtils.getRequest();
String nowParams = argsArrayToString(point.getArgs());
// 请求地å€ï¼ˆä½œä¸ºå˜æ”¾cacheçš„key值)
String url = request.getRequestURI();
// 唯一值(没有消æ¯å¤´åˆ™ä½¿ç”¨è¯·æ±‚地å€ï¼‰
String submitKey = request.getHeader(tokenProperties.getHeader());
if (StringUtils.isEmpty(submitKey)) {
submitKey = url;
}
submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
// å”¯ä¸€æ ‡è¯†ï¼ˆæŒ‡å®škey + 消æ¯å¤´ï¼‰
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
String key = RedisUtils.getCacheObject(cacheRepeatKey);
if (key == null) {
RedisUtils.setCacheObject(cacheRepeatKey, "", interval, TimeUnit.MILLISECONDS);
} else {
throw new ServiceException(repeatSubmit.message());
}
}
/**
* 傿•°æ‹¼è£…
*/
private String argsArrayToString(Object[] paramsArray) {
StringBuilder params = new StringBuilder();
if (paramsArray != null && paramsArray.length > 0) {
for (Object o : paramsArray) {
if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
try {
params.append(JsonUtils.toJsonString(o)).append(" ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return params.toString().trim();
}
/**
* åˆ¤æ–æ˜¯å¦éœ€è¦è¿‡æ»¤çš„对象。
*
* @param o 对象信æ¯ã€‚
* @return 如果是需è¦è¿‡æ»¤çš„对象,则返回trueï¼›å¦åˆ™è¿”回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}