Skip to content

限访名单

作者:唐亚峰 | battcn
字数统计:467 字

顾名思义,限流就是限制流量。通过限流,我们可以很好地控制系统的 QPS,从而达到保护系统的目的。 常见的限流算法有:计数器算法,漏桶(Leaky Bucket)算法,令牌桶(Token Bucket)算法。

Spring Cloud Gateway 官方提供了 RequestRateLimiterGatewayFilterFactory 过滤器工厂,使用 RedisLua 脚本实现了令牌桶的方式。

添加配置

yaml
spring:
  application:
    name: wemirr-platform-gateway
  cloud:
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        - id: wemirr-platform-authority
          uri: lb://wemirr-platform-authority
          predicates:
            - Path=/authority/**
          filters:
            # 黑白名单过滤器
            - name: BlackWhiteList
              args:
                type: BLACK_LIST
                ip-list: 127.0.0.1,0:0:0:0:0:0:0:1
                ignore-intranet: true

实现代码

须知

这个黑白名单拦截器是简单的实现,具体可以基于设计思路自行扩展

java
package com.wemirr.platform.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.net.HttpHeaders;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;

/**
 * XForwardedRemoteAddressResolver
 * 黑白名单过滤器
 *
 * @author Levin
 */
@Slf4j
@Order(99)
@Configuration
public class BlackWhiteListGatewayFilterFactory extends AbstractGatewayFilterFactory<BlackWhiteListGatewayFilterFactory.Config> {

    private static final String DEFAULT_FILTER_NAME = "BlackWhiteList";

    @Override
    public String name() {
        return DEFAULT_FILTER_NAME;
    }

    public BlackWhiteListGatewayFilterFactory() {
        super(Config.class);
    }


    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            InetSocketAddress remoteAddress = XForwardedRemoteAddressResolver.maxTrustedIndex(1).resolve(exchange);
            final InetAddress inetAddress = remoteAddress.getAddress();
            String ip = inetAddress.getHostAddress();
            log.debug("[访问者IP地址] - [{}]", ip);
            if (config.isIgnoreIntranet() && inetAddress.isSiteLocalAddress()) {
                log.info("[忽略内网IP] - {}", inetAddress.isSiteLocalAddress());
                return chain.filter(exchange);
            }
            if (config.type == BlackWhiteListType.BLACK_LIST) {
                boolean access = config.getIpList().contains(ip);
                if (access) {
                    log.warn("[访问受限,该地址在黑名单列表] - [{}]", ip);
                    return accessRestricted(exchange);
                }
            } else if (config.type == BlackWhiteListType.WHITE_LIST) {
                boolean access = config.getIpList().contains(ip);
                if (access) {
                    return chain.filter(exchange);
                } else {
                    log.warn("[访问受限,该地址不在白名单列表] - [{}]", ip);
                    return accessRestricted(exchange);
                }
            }
            return chain.filter(exchange);
        };
    }

    private Mono<Void> accessRestricted(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        JSONObject result = new JSONObject();
        result.put("messageId", HttpStatus.FORBIDDEN.value());
        result.put("message", "访问受限,请联系管理员");
        result.put("successful", false);
        result.put("timestamp", System.currentTimeMillis());
        return response.writeWith(Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes(result))));
    }

    @Data
    public static class Config {

        private Integer maxTrustedIndex = 1;
        private BlackWhiteListType type;
        private boolean ignoreIntranet;
        private List<String> ipList;
    }

    @AllArgsConstructor
    public enum BlackWhiteListType {
        /**
         * 黑名单
         */
        BLACK_LIST,
        /**
         * 白名单
         */
        WHITE_LIST;

    }

}