当前位置:网站首页>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

边栏推荐
- R语言使用glm函数构建泊松对数线性回归模型处理三维列联表数据构建饱和模型、使用exp函数和coef函数获取模型所有变量的事件密度比(Incidence Density Ratio,IDR)并解读
- SRM supplier collaborative management system function introduction
- How MySQL queries character set codes of tables
- 育润多维发力慈善领域,勇抗企业公益大旗
- How to solve MySQL 1045 error in Linux
- R language ggplot2 visualization: use the patchwork package (directly use the plus sign +) to horizontally combine a ggplot2 visualization result and a plot function visualization result to form the f
- selenium 组合键操作
- Teach you how to install the latest version of mysql8.0 database on windows, nanny level teaching
- Split palindrome string [dp + DFS combination]
- Leetcode daily question - 535 Encryption and decryption of tinyurl
猜你喜欢

selenium上传文件

传承中华美德,关注中老年大健康,育润奶粉敬老情浓

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

mysql在linux中2003错误如何解决

Force deduction daily question 06.29 add two numbers

Teach you how to install the latest version of mysql8.0 database on windows, nanny level teaching

How to solve the 2003 error of MySQL in Linux

selenium 文件上传方法

序列检测器
![Split palindrome string [dp + DFS combination]](/img/7b/221b000984977508f849e19802c2c2.png)
Split palindrome string [dp + DFS combination]
随机推荐
剑桥大学教授:经常吃早餐害处多,很危险 - 知乎
序列检测器
Cross border independent station language Unicode to Hebrew
Force deduction daily question 06.29 add two numbers
国外LEAD赚钱,做个网站真的很简单
R language uses GLM function to build Poisson logarithm linear regression model, processes three-dimensional contingency table data to build saturation model, uses exp function and coef function to ob
OpenFeign使用步骤 轮询策略与权重 log4j使用 openFeign拦截器的配置
How to create a virtual image
Face recognition 4- research on Baidu commercial solutions
Opencv+yolo-v3 for target tracking
Tencent cloud released orbit, an automated delivery and operation and maintenance product, to promote enterprise applications to be fully cloud native
Analyze the implementation principle of zero copy mechanism, applicable scenarios and code implementation
布隆过滤器:
双亲委派机制
R language uses user-defined functions to write deep learning leaky relu activation functions and visualize leaky relu activation functions
Maidong Internet won the bid of Dajia Insurance Group
R语言epiDisplay包的aggregate函数将数值变量基于因子变量拆分为不同的子集,计算每个子集的汇总统计信息、aggregate.data.frame函数包含缺失值的情况下分组统计结果为NA
How to use the chart control of the b/s development tool devextreme - customize the axis position?
测试dble split功能执行+导入耗时shell脚本参考
Teach you how to install the latest version of mysql8.0 database on windows, nanny level teaching