当前位置:网站首页>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
边栏推荐
- Younger sister Juan takes you to learn JDBC - 2-day dash Day1
- Mysql中锁的使用场景是什么
- Walk with love, educate and run poor families, and promote public welfare undertakings
- Bottom level internal skill cultivation
- 剑桥大学教授:经常吃早餐害处多,很危险 - 知乎
- R语言使用自定义函数编写深度学习Leaky ReLU激活函数、并可视化Leaky ReLU激活函数
- Epoll analysis
- The aggregate function in the epidisplay package of R language divides numerical variables into different subsets based on factor variables, and calculates the summary statistics and aggregate data. W
- Browser large screen capture
- 与爱同行,育润走进贫困家庭,助推公益事业
猜你喜欢
mysql. What is the concept of sock
How to use the chart control of the b/s development tool devextreme - customize the axis position?
linux中mysql 1045错误如何解决
mysql如何查询表的字符集编码
Openfeign use step polling strategy and weight log4j configuration of openfeign interceptor
剑桥大学教授:经常吃早餐害处多,很危险 - 知乎
Online text digit recognition list summation tool
Opencv+yolo-v3 for target tracking
Basic operations such as MySQL startup under Windows platform
双亲委派机制
随机推荐
力扣每日一题 06.29 两数相加
Use SSH to pull codes
How to solve the 2003 error of MySQL in Linux
L'intercepteur handlerinterceptor personnalisé permet l'authentification de l'utilisateur
与爱同行,育润走进贫困家庭,助推公益事业
Web Scraping with Beautiful Soup for Data Scientist
Younger sister Juan takes you to learn JDBC - 2-day dash Day1
Selenium upload file
关于Go中两个模块互相调用的场景解决方案
How to create and delete MySQL triggers
SRM供应商协同管理系统功能介绍
Visual Studio插件CodeRush正式发布v22.1——优化调试可视化工具
[Oracle] basic knowledge interview questions
What value can SRM systems bring to the enterprise?
育润多维发力慈善领域,勇抗企业公益大旗
The dplyr package filter function of R language filters the data in dataframe data through combinatorial logic (and logic). The content of one field is equal to one of the specified vectors, and the v
Industry application of smart city based on GIS 3D visualization
Master slave replication of MySQL
Inherit Chinese virtues, pay attention to the health of the middle-aged and the elderly, and Yurun milk powder has strong respect for the elderly
selenium上传文件