重复提交定义:
用户在一次请求未完成期间,不可重复请求,即用户一次请求服务器未完成响应时,不可再次发起同样的请求
实现方案:
请求开始时 将用户的id+请求地址+请求参数 作为key 缓存在redis中,请求完成后移除redis缓存的key
实现代码:
package com.yd.zuul.filter;
import com.alibaba.fastjson.JSON;
import com.hcloud.common.ApiResult;
import com.hcloud.common.Code;
import com.hcloud.utils.Md5Utils;
import com.hcloud.utils.redis.RedisUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
/**
* 重复请求进行拦截处理
* 并把当前请求进行重复标识 ,
* 请求完成后进行移除 (详见 RepeatRequestAfterFilter )
* @author kiss
*/
@Component
public class RepeatRequestFilter extends ZuulFilter {
public static final String REPEAT_FLAG = "REPEAT_FLAG";
private static final Logger logger = LoggerFactory.getLogger(RepeatRequestFilter.class);
@Autowired(required = false)
private RedisUtil redisUtil;
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return -5;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
final RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if (!ctx.getRequest().getRequestURI().contains("oauth/token")) {//登录认证过滤
String url = request.getRemoteAddr() + request.getServletPath();
try {
String params = null;
//表单中获取参数
Map<String, String[]> parameterMap = request.getParameterMap();
if (!parameterMap.isEmpty()) {
params = JSON.toJSONString(parameterMap);
}
//body中获取参数
if (!ctx.isChunkedRequestBody()) {
ServletInputStream inp = ctx.getRequest().getInputStream();
if (inp != null) {
if (StringUtils.isEmpty(params)) {
params = IOUtils.toString(inp, "utf-8");
} else {
params += IOUtils.toString(inp, "utf-8");
}
}
}
//获取用户信息token
String userToken = request.getHeader("Authorization");
// 验证参数是否被修改 end
//为空添加入Redis,不为空 返回错误信息重复提交
String md5Key = Md5Utils.encryptMD5(userToken + url + params);
request.setAttribute(REPEAT_FLAG, md5Key);
if (StringUtils.isEmpty(redisUtil.get(md5Key))) {
redisUtil.set(md5Key, md5Key, 5L);//默认5秒后可以再次请求同一接口
} else {
ApiResult apiResult = new ApiResult();
apiResult.code(Code.error).message("您的请求太频繁,请稍后尝试!");
ctx.getResponse().setCharacterEncoding("utf-8");
ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
ctx.setResponseStatusCode(200);// 返回错误码
ctx.setResponseBody(JSON.toJSONString(apiResult));// 返回错误内容
ctx.set("isSuccess", false);
logger.info("请求过于频繁");
logger.info("客户地址:{} 请求地址:{} 请求方式 {}", request.getRemoteHost(), request.getRequestURL().toString(), request.getMethod());
logger.info("params:{} ", params);
return null;
}
} catch (Exception e) {
logger.warn("{}重复请求校验出错:{}", request.getRequestURL(), e.getMessage());
}
}
return null;
}
}
package com.yd.zuul.filter;
import com.hcloud.utils.redis.RedisUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 重复请求处理 用户请求完成后清除重复标识
*
* @author kiss
*/
@Component
public class RepeatRequestAfterFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(RepeatRequestFilter.class);
@Autowired(required = false)
private RedisUtil redisUtil;
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return -6;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if (request.getAttribute(RepeatRequestFilter.REPEAT_FLAG) != null) {
//logger.info("移除重复提交标识");
String key = request.getAttribute(RepeatRequestFilter.REPEAT_FLAG).toString();
redisUtil.remove(key);
request.removeAttribute(RepeatRequestFilter.REPEAT_FLAG);
}
//logger.info("时间:{}", new SimpleDateFormat("hh:mm:ss.SSS").format(System.currentTimeMillis()));
return null;
}
}