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);
}