[toc]
使用说明
- 强依赖 redisTemplate ,但 spring-boot-starter-redis scope 为 provided 项目在使用时候需要手动引入改依赖
- 需要配置 redis 连接
spring.redis.host=localhost
spring.redis.port=6379
- 启用 @EnableResubmitCheck 注解
- 该校验非强制校验,需要在校验的目标方法上给启用 @ResubmitCheck
- 校验可选参数项
/**
* md5 是否包含header中的认证信息 ,默认为true
*
* @return
*/
boolean includeAuthorization() default true;
/**
* 重复数据提交间隔时间,默认30s
*
* @return
*/
int expireTime() default 30;
核心代码
package com.runstyle.common.resubmit;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PropertyFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Closeable;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Aspect
@Component
@Slf4j
public class ResubmitAspect {
@Autowired
RedisTemplate<String, String> redisTemplate;
/**
* 调用方法前输出日志
*
* @param joinPoint
*/
@Around(value = "@annotation( com.runstyle.common.resubmit.ResubmitCheck)")
public Object methodBefore(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
ResubmitCheck annotation = method.getAnnotation(ResubmitCheck.class);
String data = getRequestData(joinPoint);
HttpServletRequest request = (HttpServletRequest) RequestContextHolder.currentRequestAttributes().resolveReference(RequestAttributes.REFERENCE_REQUEST);
if (annotation.includeAuthorization()) {
String authorization = request.getHeader("Authorization");
data += ":" + authorization;
}
data += ":" + request.getRequestURI();
String key = "resubmit:" + DigestUtils.md5Hex(data);
boolean s1 = redisTemplate.opsForValue().setIfAbsent(key, "");
if (!s1) {
//不通过
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
response.setStatus(403);
PrintWriter writer = response.getWriter();
data += ":left" + redisTemplate.getExpire(key, TimeUnit.SECONDS);
String fullName = methodSignature.getDeclaringTypeName() + "." + methodSignature.getName();
log.warn("重复提交 method:{},data:{}", fullName, data);
writer.write("resubmit:" + data);
return null;
}
redisTemplate.expire(key, annotation.expireTime(), TimeUnit.SECONDS);
Object proceed = joinPoint.proceed();
return proceed;
}
/**
* 获取请求参数
*
* @param joinPoint
* @return
*/
private String getRequestData(ProceedingJoinPoint joinPoint) {
//排除序列化字段
Object[] args = joinPoint.getArgs();
List<Object> collect = Stream.of(args).filter(o -> !(o instanceof Closeable || o instanceof ServletRequest || o instanceof ServletResponse)).collect(Collectors.toList());
PropertyFilter propertyFilter = (object, name, value) -> {
if (object instanceof Closeable || object instanceof ServletRequest || object instanceof ServletResponse ||
value instanceof Closeable || value instanceof ServletRequest || value instanceof ServletResponse) {
// 过滤掉
return false;
}
return true;
};
return JSON.toJSONString(collect, propertyFilter);
}
}
demo
启动类
@SpringBootApplication
@EnableResubmitCheck
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
controller
@RestController
@Slf4j
public class TestController {
@RequestMapping("/test")
@ResubmitCheck
public String test(@RequestBody(required = false) String body, String param1, HttpServletRequest request, HttpServletResponse response) {
log.info("body:{}", body);
log.info("param1:{}", param1);
return "ok";
}
}
执行效果
浏览器
resubmit:["dddd2","3"]:null:/test:left29
控制台
2019-04-18 17:43:48.389 INFO 10572 --- [nio-8080-exec-4] com.example.demo.TestController : body:dddd2
2019-04-18 17:43:48.390 INFO 10572 --- [nio-8080-exec-4] com.example.demo.TestController : param1:3
2019-04-18 17:43:56.651 WARN 10572 --- [nio-8080-exec-5] c.r.common.resubmit.ResubmitAspect : 重复提交 method:com.example.demo.TestController.test,data:["dddd2","3"]:null:/test:left21
2019-04-18 17:45:18.621 INFO 10572 --- [nio-8080-exec-7] com.example.demo.TestController : body:dddd2
2019-04-18 17:45:18.621 INFO 10572 --- [nio-8080-exec-7] com.example.demo.TestController : param1:3
2019-04-18 17:45:19.489 WARN 10572 --- [nio-8080-exec-8] c.r.common.resubmit.ResubmitAspect : 重复提交 method:com.example.demo.TestController.test,data:["dddd2","3"]:null:/test:left29
网友评论