小记:使用Zuul网关过滤器进行基础的过滤拦截

601 阅读3分钟

Zuul

当微服务使用了Zuul网关之后,那么前端发来的全部请求都会进入到这里,不过微服务之间的通信不会经过Zuul

简单的过滤器构建

默认Zuul的环境配置已经配置完成

/**
 * 构建zuul的自定义过滤器
 */
@Component
public class MyFilter extends ZuulFilter { 

    /**
     * 定义过滤器类型
     *      pre:    在请求被路由之前执行
     *      route:  在路由请求的时候执行
     *      post:   请求被路由以后执行
     *      error:  处理请求时发生错误执行
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器的执行顺序,可以配置多个过滤器
     * 执行顺序从小到大 1>2>3...
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 是否开启过滤器
     *      true:使用
     *      false:禁用
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤器业务实现
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("display pre zuul filter...");
        return null;
    }
}

对于一定时间内访问次数的限制

基于Zuul网关的过滤拦截,尝试实现一个对于IP频繁访问的限制,例如我们可以限制一个ip在10秒内只能访问10次,不然就当作非法请求进行限制

首先在配置文件定义

blackIP:
  continueCounts: ${counts:10}    # ip连续请求的次数
  timeInterval: ${interval:10}    # ip判断的时间间隔,单位:秒
  limitTimes: ${times:15}         # 限制的时间,单位:秒

过滤器业务的开发,在 run 方法中进行业务处理,整合redis进行开发,使用了http的工具类获取IP,以及RedisOperator操作redis

@Override
public Object run() throws ZuulException {
    System.out.println("执行【IP】Zuul过滤器...");

    // 获得上下文对象requestContext
    RequestContext requestContext = RequestContext.getCurrentContext();
    //获取request
    HttpServletRequest request = requestContext.getRequest();

    // 获得ip(使用了工具类)
    String ip = IPUtil.getRequestIp(request);

    /**
     * 需求:
     * 判断ip在10秒内请求的次数是否超过10次,
     * 如果超过,则限制访问15秒,15秒过后再放行
     */
    final String ipRedisKey = "zuul-ip:" + ip;
    final String ipRedisLimitKey = "zuul-ip-limit:" + ip;

    // 获得剩余的限制时间
    // Redis TTL:当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。 否则,以秒为单位,返回 key 的剩余生存时间。
    long limitLeftTime = redis.ttl(ipRedisLimitKey);
    // 如果剩余时间还存在,说明这个ip不能访问,继续等待
    if (limitLeftTime > 0) {
        stopRequest(requestContext);
        return null;
    }

	
    // 在redis中累加ip的请求访问次数
    // 一般初次请求会执行
    long requestCounts = redis.increment(ipRedisKey, 1);
    // 从0开始计算请求次数,初期访问为1,则设置过期时间,也就是连续请求的间隔时间
    if (requestCounts == 1) {
    	//设置过期时间为10秒
        redis.expire(ipRedisKey, timeInterval);
    }

    // 如果还能取得到请求次数,说明用户连续请求的次数落在10秒内
    // 一旦请求次数超过了连续访问的次数,则需要限制这个ip了
    if (requestCounts > continueCounts) {
        // 限制ip访问一段时间
        redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);

        stopRequest(requestContext);
    }
    return null;
}
/**
  对路由的控制方法
*/
private void stopRequest(RequestContext requestContext){
    // 停止继续向下路由,禁止请求通信
    requestContext.setSendZuulResponse(false);
    requestContext.setResponseStatusCode(200);
    String result = JsonUtils.objectToJson(
            GraceJSONResult.errorCustom(
                    ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP));
    requestContext.setResponseBody(result);
    requestContext.getResponse().setCharacterEncoding("utf-8");
    requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
}