当前位置:网站首页>Prevent form resubmission based on annotations and interceptors
Prevent form resubmission based on annotations and interceptors
2022-06-29 17:44:00 【llp1110】
Prevent form resubmission based on annotations and interceptors
1. Define to prevent duplicate submission of identification comments
/** * Define an annotation that needs to be marked for repeated submission verification * This annotation is used to decorate the control layer method */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RepeatSubmit {
/** * The interval between two requests ( The unit of time is redis obtain interval Value ) * @return */
int interval() default 5000;
/** * Prompt text when submitting repeatedly * @return */
String message() default " Duplicate submission is not allowed , Please try again later ";
}
2. rewrite HttpServletRequestWrapper
By default, it is not allowed to pass request.getInputStream Get the request data , Here through the custom HttpServletRequestWrapper Back up the dirty data , Customize HttpServletRequestWrapper Call the parent class request.getInputStream() Read all the data and save it in one byte In array , When the stream data is retrieved again , Self defined HttpServletRequestWrapper Will use byte Array regenerates a new stream . The backed up stream data remains in byte Array .
public class RepeatableReadRequestWrapper extends HttpServletRequestWrapper {
private final byte[] bytes;
public RepeatableReadRequestWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException {
super(request);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
bytes = request.getReader().readLine().getBytes();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public int available() throws IOException {
return bytes.length;
}
};
}
}
3. Define filters
public class RepeatableRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
if (StringUtils.startsWithIgnoreCase(request.getContentType(), "application/json")) {
RepeatableReadRequestWrapper requestWrapper = new RepeatableReadRequestWrapper(request, (HttpServletResponse) servletResponse);
filterChain.doFilter(requestWrapper,servletResponse);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
4. Encapsulates a Redis Caching tool class
@Component
public class RedisCache {
@Autowired
RedisTemplate redisTemplate;
public <T> void setCacheObject(final String key, final T value, Integer timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
public <T> T getCacheObject(final String key) {
ValueOperations<String,T> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}
}
5. Define the duplicate submission verification interceptor
@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {
public static final String REPEAT_PARAMS = "repeat_params";
public static final String REPEAT_TIME = "repeat_time";
public static final String REPEAT_SUBMIT_KEY = "repeat_submit_key";
public static final String HEADER = "Authorization";
@Autowired
RedisCache redisCache;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Determine whether it is a mapping controller Layer method directly through
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// obtain Method object
Method method = handlerMethod.getMethod();
// obtain @RepeatSubmit annotation
RepeatSubmit repeatSubmit = method.getAnnotation(RepeatSubmit.class);
// Determine whether it is null , If it is blank, it will be released directly , If it is not blank, repeat the submission verification
if (repeatSubmit != null) {
// Verify whether to submit repeatedly , If not, release , If the submission is repeated, the status code will be returned :500 message:" Text description when submitting repeatedly "
if (isRepeatSubmit(request, repeatSubmit)) {
Map<String, Object> map = new HashMap<>();
// Set status code
map.put("status", 500);
// Set description information
map.put("message", repeatSubmit.message());
// Set the response contentType And coding formats
response.setContentType("application/json;charset=utf-8");
//new ObjectMapper().writeValueAsString(map) take map Object conversion to json character string
response.getWriter().write(new ObjectMapper().writeValueAsString(map));
return false;
}
}
}
return true;
}
/** * Judge whether to submit repeatedly , return true It means repeated submission * * @param request * @param repeatSubmit * @return */
private boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit repeatSubmit) {
// Request parameter string
String nowParams = "";
if (request instanceof RepeatableReadRequestWrapper) {
try {
// Get request body
nowParams = ((RepeatableReadRequestWrapper) request).getReader().readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
// Otherwise, the request parameter is key-value Format
if (StringUtils.isEmpty(nowParams)) {
try {
// Get all the request parameters and convert them to json character string
nowParams = new ObjectMapper().writeValueAsString(request.getParameterMap());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
Map<String, Object> nowDataMap = new HashMap<>();
//key: repeat_params value: nowParams
nowDataMap.put(REPEAT_PARAMS, nowParams);
//key: repeat_time value: Time stamp
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
// / Project path /controller Layer path
String requestURI = request.getRequestURI();
//request.getHeader(“Authorization”) Bearer xxxx
String header = request.getHeader(HEADER);
//cacheKey = repeat_submit_key + / Project path /controller Layer path / xxxx
String cacheKey = REPEAT_SUBMIT_KEY + requestURI + header.replace("Bearer ", "");
// From the cache cacheKey Value
Object cacheObject = redisCache.getCacheObject(cacheKey);
if (cacheObject != null) {
Map<String, Object> map = (Map<String, Object>) cacheObject;
// Calibration parameters 、 Whether the interval meets the verification conditions for repeated submission , Return if both are satisfied true, Otherwise return to false
if (compareParams(map, nowDataMap) && compareTime(map, nowDataMap, repeatSubmit.interval())) {
return true;
}
}
// Failed to get... From cache cacheKey Value , Set to redis in
/** * @param1: cacheKey = repeat_submit_key + / Project path /controller Layer path / xxxx * @parma2: Request data Map object : Contains request parameters 、 Request timestamp * @parma3: The time between requests , It's also redis key The expiration time of * @parma4: The unit of expiration time MILLISECONDS millisecond */
redisCache.setCacheObject(cacheKey, nowDataMap, repeatSubmit.interval(), TimeUnit.MILLISECONDS);
return false;
}
/** * Whether the interval between this request and the last request exceeds the interval we set interval * If it does not exceed, the verification conditions for repeated submission are met and the result is returned true * If it exceeds, it is not satisfied , return false * @param map * @param nowDataMap * @param interval * @return */
private boolean compareTime(Map<String, Object> map, Map<String, Object> nowDataMap, int interval) {
// Time of last request
Long time1 = (Long) map.get(REPEAT_TIME);
// The time of this request
Long time2 = (Long) nowDataMap.get(REPEAT_TIME);
// Contrast time difference , If it is less than the interval between two requests we set , It means that the conditions for repeated submission are met and return true
if ((time2 - time1) < interval) {
return true;
}
// If the time interval for repeated submission is not met, return false
return false;
}
/** * Compare whether the data of this request is the same as that of the last request * @param map * @param nowDataMap * @return */
private boolean compareParams(Map<String, Object> map, Map<String, Object> nowDataMap) {
String nowParams = (String) nowDataMap.get(REPEAT_PARAMS);
String dataParams = (String) map.get(REPEAT_PARAMS);
return nowParams.equals(dataParams);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
6. Define a Dto
@Data
public class UserDto {
private String name;
private Integer age;
private String nickName;
private String skill;
}
7.Controller Test class
@RestController
public class HelloController {
@PostMapping("/hello")
@RepeatSubmit(interval = 10000)
public String hello(@RequestBody String json) {
return json;
}
@RepeatSubmit
@Authorization
@PostMapping("/test")
public String authorization(@RequestBody UserDto userDto) {
System.out.println(userDto);
return "succes";
}
}
test result

边栏推荐
猜你喜欢

Visio标注、批注位置

MATLAB 最远点采样(FPS)

0 basic self-study STM32 (wildfire) - register lit LED

底层内功修养

How to create and delete MySQL triggers

如何使用B/S开发工具DevExtreme的图表控件 - 自定义轴位置?

Online text digit recognition list summation tool

Force deduction daily question 06.29 add two numbers

VB.Net读写NFC Ntag标签源码

selenium 文件上传方法
随机推荐
序列检测器
mongoTemplate - distinct 使用
跨境独立站语言unicode转希伯来语
剖析下零拷贝机制的实现原理,适用场景和代码实现
Li Kou today's question -535 Encryption and decryption of tinyurl
Bottom level internal skill cultivation
[webdriver] upload files using AutoIT
0 basic self-study STM32 (wildfire) - register lit LED
mysql查询视图命令是哪个
Industry application of smart city based on GIS 3D visualization
SRM供应商协同管理系统功能介绍
Tencent cloud released orbit, an automated delivery and operation and maintenance product, to promote enterprise applications to be fully cloud native
mysql在linux中2003错误如何解决
小程序容器是什么技术?能助力物联网企业红海突围?
R语言ggplot2可视化:使用patchwork包(直接使用加号+)将一个ggplot2可视化结果和一个plot函数可视化结果横向组合起来形成最终结果图
Repair of JSON parsing errors in a collection
Use SSH to pull codes
第42期:MySQL 是否有必要多列分区
Visual Studio插件CodeRush正式发布v22.1——优化调试可视化工具
Analyze the implementation principle of zero copy mechanism, applicable scenarios and code implementation