Spring Cloud ZuulFilter 简单实现防重复提交

重复提交定义:

用户在一次请求未完成期间,不可重复请求,即用户一次请求服务器未完成响应时,不可再次发起同样的请求

实现方案:

请求开始时 将用户的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;
    }
}

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值