实现基于redis的表单重复提交拦截器
首先编写request的封装类,用于重复读取request中的body流
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.test.common.enums.RedisKeyEnum;
import com.test.common.utils.AjaxResult;
import com.test.common.utils.RedisComponent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import javax.annotation.Resource;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@WebFilter(filterName = "RepeatedSubmitFilter", urlPatterns = {"/*"})
@Order(2)
/**
* @author Neal.Zhi
* @description 防止表单重复提交的过滤器
* @date 2020/09/17 14:10
**/
public class RepeatedSubmitFilter implements Filter {
private static final AjaxResult ajaxResult = AjaxResult.failNoTime("请勿重复提交!");
@Resource
RedisComponent redisComponent;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取当前的时间戳
long currentTimeMillis = System.currentTimeMillis();
//使用包装类封装request,解决servlet无法重复获取body流的问题
MultiReadHttpServletRequest requestWrapper = new MultiReadHttpServletRequest(request);
//请求参数
String queryString = "";
//请求类型
String contentType = requestWrapper.getContentType();
//请求url
String requestURI = requestWrapper.getRequestURI();
//请求尾接参数
String requestQueryString = requestWrapper.getQueryString();
//获取token
String token = requestWrapper.getHeader("Authorization");
//如果token为空放行
if (StringUtils.isBlank(token)) {
filterChain.doFilter(requestWrapper, servletResponse);
return;
}
//获取请求参数
//如果url有尾接参数
if (StringUtils.isNotBlank(requestQueryString)) {
queryString = requestQueryString;
}
//如果为表单请求
if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(contentType)) {
queryString = requestWrapper.getBodyStr();
}
//如果为json请求
if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
queryString = requestWrapper.getBodyStr();
}
//如果参数为空放行
if (StringUtils.isBlank(queryString)) {
filterChain.doFilter(requestWrapper, servletResponse);
return;
}
//将参数进行散列加密
String queryStringMd5 = SecureUtil.md5(queryString);
String redisMapKey = token + ":" + requestURI + ":" + queryStringMd5;
String redisKey = RedisKeyEnum.REPEATED.getValue();
//在redis查询此接口是否被1秒内被查询过
Object hget = redisComponent.hget(redisKey, redisMapKey);
if (hget != null) {
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ServletOutputStream outputStream = response.getOutputStream();
String result = JSON.toJSONString(ajaxResult);
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
return;
}
//存入1秒
redisComponent.hset(redisKey, redisMapKey, currentTimeMillis, 1);
filterChain.doFilter(requestWrapper, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) {
//do nothing
}
@Override
public void destroy() {
//do nothing
}
}
再编写过滤器,并且将request进行封装使用
注意:doFilter中放行的request一定要用封装类,否则后续使用request获取流或者reader时,会进行报错
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.test.common.enums.RedisKeyEnum;
import com.test.common.utils.AjaxResult;
import com.test.common.utils.RedisComponent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import javax.annotation.Resource;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@WebFilter(filterName = "RepeatedSubmitFilter", urlPatterns = {"/*"})
@Order(2)
/**
* @author Neal.Zhi
* @description 防止表单重复提交的过滤器
* @date 2020/09/17 14:10
**/
public class RepeatedSubmitFilter implements Filter {
private static final AjaxResult ajaxResult = AjaxResult.failNoTime("请勿重复提交!");
//这里可以注入你自己业务中的redis操作类
@Resource
RedisComponent redisComponent;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取当前的时间戳
long currentTimeMillis = System.currentTimeMillis();
//使用包装类封装request,解决servlet无法重复获取body流的问题
MultiReadHttpServletRequest requestWrapper = new MultiReadHttpServletRequest(request);
//请求参数
String queryString = "";
//请求类型
String contentType = requestWrapper.getContentType();
//请求url
String requestURI = requestWrapper.getRequestURI();
//请求尾接参数
String requestQueryString = requestWrapper.getQueryString();
//获取token(你可以根据你的业务自行获取相关token)
String token = requestWrapper.getHeader("Authorization");
//如果token为空放行
if (StringUtils.isBlank(token)) {
filterChain.doFilter(requestWrapper, servletResponse);
return;
}
//获取请求参数
//如果url有尾接参数
if (StringUtils.isNotBlank(requestQueryString)) {
queryString = requestQueryString;
}
//如果为表单请求
if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(contentType)) {
queryString = requestWrapper.getBodyStr();
}
//如果为json请求
if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
queryString = requestWrapper.getBodyStr();
}
//如果参数为空放行
if (StringUtils.isBlank(queryString)) {
filterChain.doFilter(requestWrapper, servletResponse);
return;
}
//将参数进行散列加密
String queryStringMd5 = SecureUtil.md5(queryString);
String redisMapKey = token + ":" + requestURI + ":" + queryStringMd5;
//这里可以定义你自己的rediskey常量
String redisKey = RedisKeyEnum.REPEATED.getValue();
//在redis查询此接口是否被1秒内被查询过
Object hget = redisComponent.hget(redisKey, redisMapKey);
if (hget != null) {
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ServletOutputStream outputStream = response.getOutputStream();
String result = JSON.toJSONString(ajaxResult);
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
return;
}
//存入1秒
redisComponent.hset(redisKey, redisMapKey, currentTimeMillis, 1);
filterChain.doFilter(requestWrapper, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) {
//do nothing
}
@Override
public void destroy() {
//do nothing
}
}