.Net 코어 제한 흐름 의 실현 예시

환경
1.vs2019
2..Net Core 3.1
3.AspNetCoreRateLimit 4.0.1 참조
2.기초 사용
1.설정
Startup 파일 에 다음 과 같이 설정 합 니 다.설정 항목 을 모두 앞 에 놓 습 니 다.

 public void ConfigureServices(IServiceCollection services)
 {
  //  appsettings.json   ip        
  services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
  //  appsettings.json   ip    
  services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimiting:IpRateLimitPolicies"));
  //  appsettings.json              
  services.Configure<ClientRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
  //  appsettings.json          
  services.Configure<ClientRateLimitPolicies>(Configuration.GetSection("IpRateLimiting:ClientRateLimitPolicies"));
  //           
  services.AddInMemoryRateLimiting();
  //   (   、        )
  services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
  //  clientid ip     ,        ,     
  //services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  //  ip            
  //      ,        ,    ip                     ,         
  app.UseIpRateLimiting();
  app.UseClientRateLimiting();
}
2.규칙 설정
규칙 의 설정 은 두 가지 로 나 뉜 다.IP 를 통 해 흐름 을 제한 하고 클 라 이언 트 를 통 해 흐름 을 제한 하 는 것 이다.모두 프로필 을 통 해 파 라 메 터 를 설정 합 니 다.apptsettings.json 에서 다음 과 같이 설정 합 니 다(다른 프로필 을 설정 할 수도 있 습 니 다).

"IpRateLimiting": {
    "EnableEndpointRateLimiting": false,
    "StackBlockedRequests": false,
    "RealIpHeader": "X-Real-IP",
    "ClientIdHeader": "X-ClientId",
    "HttpStatusCode": 429,
    //"IpWhitelist": [ "198.0.0.1", "::1/10", "192.168.0.13/24" ],
    "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
    "ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
    "QuotaExceededResponse": {
      "Content": "{{\"code\":429,\"msg\":\"Visit too frequently, please try again later\",\"data\":null}}",
      "ContentType": "application/json;utf-8",
      "StatusCode": 429
    },
    "GeneralRules": [
      {
        "Endpoint": "*",
        "Period": "1s",
        "Limit": 2
      }
    ],
    "ClientRateLimitPolicies": {
      "ClientRules": [
        {
          "ClientId": "client-id-1",
          "Rules": [
            {
              "Endpoint": "*",
              "Period": "1s",
              "Limit": 10
            },
            {
              "Endpoint": "*",
              "Period": "15m",
              "Limit": 200
            }
          ]
        }
      ]
    },
    "IpRateLimitPolicies": {
      "IpRules": [
        {
          "Ip": "84.247.85.224",
          "Rules": [
            {
              "Endpoint": "*",
              "Period": "1s",
              "Limit": 10
            },
            {
              "Endpoint": "*",
              "Period": "15m",
              "Limit": 200
            }
          ]
        }
      ]
    }
  }

각 설정 항목 의 설명 은 다음 과 같 습 니 다.
 EnableEndpoint Rate Limiting:true 로 설정 하면 점 규칙 이*일 때 GET,POST 등 모든 서술 어 는 각각 제한 횟수 를 가진다.예 를 들 어*:/api/values 클 라 이언 트 가 초당 GET/api/values 5 회 호출 제한 을 설정 하면 초당 5 회 호출 할 수 있 지만 5 회 PUT/api/values 를 호출 할 수 있 습 니 다.
false 로 설정 하면 상기 예 에서 GET,POST 등 공유 요청 횟수 가 제 한 됩 니 다.횟수 제한 설정 을 공유 할 지 여부 입 니 다.이 매개 변 수 를 false 로 설정 할 때 점 만 별표*로 설정 하 는 규칙 이 유효 하고 다른 규칙 은 유효 하지 않 으 며 true 로 설정 할 때 모든 규칙 이 유효 합 니 다.
StackBlocked Requests:false 로 설정 한 경우 거 부 된 요청 은 카운터 에 가입 하지 않 습 니 다.예 를 들 어 1 초 에 세 개의 요청 이 있 으 면 스 트림 제한 규칙 이 각각 1 초 에 한 번,1 분 에 세 번 으로 설정 되 어 있 으 면 거 부 된 두 개의 요청 은 1 분 에 세 번 의 규칙 에 기록 되 지 않 습 니 다.즉,이 인 터 페 이 스 를 두 번 호출 할 수 있 습 니 다.true 로 설정 하면 거 부 된 요청 도 계수 기 를 추가 합 니 다.상기 예 와 같이 1 분 안에 호출 할 수 없습니다.세 번 모두 기록 합 니 다.
RealIpHeader:설정 항목 IP 화이트 리스트 IpWhitelist 와 조합 하여 사용 합 니 다.이 매개 변수 가 정의 하 는 요청 헤더 이름 이 요청 에 존재 하고 이 매개 변수 내용 이 IP 화이트 리스트 의 IP 이면 스 트림 규칙 에 제한 을 받 지 않 습 니 다.
Client IdHeader:설정 항목 클 라 이언 트 화이트 리스트 Client IdHeader 와 조합 하여 사용 합 니 다.이 매개 변수 가 정의 하 는 요청 헤더 이름 이 요청 에 존재 하고 이 매개 변수 내용 이 클 라 이언 트 화이트 리스트 의 이름 이 라면 스 트림 규칙 에 제한 을 받 지 않 습 니 다.
HttpStatusCode:http 요청 스 트림 후 되 돌아 오 는 코드 입 니 다.
IpWhitelist:IP 화이트 리스트,필드 지원 Ip v4 와 v6,예 를 들 어"198.0.0.0.1",":1/10","192.168.0.13/24"등.RealIpHeader 매개 변수 에 맞 춰 사용 할 수 있 고 단독으로 사용 할 수 있 으 며 요청 한 ip 는 이 화이트 리스트 규칙 에 부합 되 며 제한 되 지 않 습 니 다.
Endpoint Whitelist:터미널 화이트 리스트 입 니 다.이 터미널 규칙 에 맞 는 요청 은 스 트림 규칙 의 영향 을 받 지 않 습 니 다.예 를 들 어"get:/api/values"는 GET 가 요청 한 api/values 인터페이스 가 영향 을 받 지 않 음 을 표시 합 니 다.*모든 종류의 요청 을 표시 합 니 다.
Client Whitelist:클 라 이언 트 화이트 리스트,Client IdHeader 파라미터 와 함께 사용 하고 클 라 이언 트 의 이름 을 설정 합 니 다.
QuotaExceed Response:스 트림 제한 후의 반환 값 설정,내용,상태 코드 등 을 되 돌려 줍 니 다.
General Rules:유 니 버 설 규칙 설정 입 니 다.세 개의 매개 변 수 는 Endpoint,Period,Limit 입 니 다.
Endpoint 터미널 형식 은{HTTPVerb}:{PATH},get:/api/values 와 같은 모든 HTTP 동 사 를 별표 로 찾 을 수 있 습 니 다.
Period 기간 형식 은{INT}{PERIODTYPE},다음 기간 유형 중 하 나 를 사용 할 수 있 습 니 다:s,m,h,d,각각 초 분 시 일 입 니 다.
Limit 는{LONG}형식 으로 접근 횟수 를 제한 합 니 다.
Client RateLimit Policies:클 라 이언 트 의 흐름 을 제한 하 는 특수 한 설정 입 니 다.규칙 은 일반적인 규칙 과 같이 설정 되 어 있 습 니 다.클 라 이언 트 IdHeader 에 맞 춰 요청 헤더 에서 사용 해 야 합 니 다.app.UseClient RateLimiting()을 사용 해 야 합 니 다.사용 하지 않 으 면 유효 하지 않 습 니 다.이 매개 변수 이름 은 변경 할 수 있 습 니 다.통용 규칙 과 특수 규칙 은 같은 우선 순위 이다.
Ip RateLimitPolicies:IP 제한 흐름 의 특수 설정 입 니 다.규칙 은 일반적인 규칙 과 같이 설정 되 어 있 습 니 다.RealIpHeader 와 함께 요청 헤더 에서 사용 해 야 합 니 다.app.UseIp RateLimiting()을 사용 해 야 합 니 다.사용 하지 않 으 면 유효 하지 않 습 니 다.이 매개 변수 이름 은 변경 할 수 있 습 니 다.통용 규칙 과 특수 규칙 은 같은 우선 순위 이다.
3.특수 규칙 사용
IP 와 클 라 이언 트 특수 규칙 의 사용 은 Program 파일 의 프로그램 입 구 를 다음 과 같이 개조 하여 각자 의 특수 규칙 을 보 냅 니 다.

public static async Task Main(string[] args)
{
  IWebHost webHost = CreateWebHostBuilder(args).Build();
  using (var scope = webHost.Services.CreateScope())
  {
    var clientPolicyStore = scope.ServiceProvider.GetRequiredService<IClientPolicyStore>();
    await clientPolicyStore.SeedAsync();

    var ipPolicyStore = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();
    await ipPolicyStore.SeedAsync();
  }
  await webHost.RunAsync();
}  

Configure Services 에서 설정 파 라 메 터 를 읽 은 다음 Startup 파일 의 Configure 방법 으로 app.UseIp RateLimiting()또는 app.UseClient RateLimiting()이 IP 특수 규칙 이나 클 라 이언 트 특수 규칙 을 시작 하 는 경우 먼저 실 행 된 것 이 적 용 됩 니 다.
 3.복귀 요청
스 트림 제한 이 시 작 된 후 스 트림 제한 규칙 을 실행 하 는 리 턴 헤드 에는 세 개의 매개 변수 가 있 습 니 다.
X-Rate-limit-limit:현재 시간,예 를 들 어 1d.
X-Rate-limit-Reamining:요청 가능 횟수 가 남 았 습 니 다.
X-Rate-limit-Reset:다음 요청 횟수 초기 화 시간.
여러 개의 제한 규칙 은 가장 긴 주기의 규칙 으로 표 시 됩 니 다.
설정 파일 에 반환 정 보 를 설정 합 니 다.알림 정 보 를 되 돌려 주 는 것 외 에 제한 규칙 알림 도 되 돌려 줍 니 다.다음 과 같 습 니 다.

"Content": "{{\"code\":429,\"msg\":\"      , {1}{0} ,  {2}    \",\"data\":null}}",
{0}현재 저지 규칙 에 규정된 횟수 를 교체 할 수 있 습 니 다.{1}시간 구간 의 단위 s,h 등 을 교체 할 수 있 습 니 다.{2}몇 초 교체 한 후 해당 단 위 를 하늘 이나 시간 등 으로 환산 할 수 있 습 니 다.
4.Redis 저장 소 사용
흐름 제한 규칙 등 은 현재 메모리 로 저장 되 어 있 으 며,우 리 는 실제 와 결합 하여 redis 저장 소 를 사용 할 것 입 니 다.Microsoft.Extensions.Cacing.Redis 를 사용 하면 이런 목적 을 달성 할 수 있 습 니 다.
그러나 성능 에 문제 가 있 을 것 같 아서 우리 가 교체 하고 CSRedis 로 포장 하 는 방법 을 사 용 했 지만 여기 서 는 설명 하지 않 았 다.
저 희 는 세 가지 데이터 1,방문 계수 2,ip 특수 규칙 3,클 라 이언 트 특수 규칙 을 캐 시 합 니 다.
1.방문 계수

public class RedisRateLimitCounterStore : IRateLimitCounterStore
    {
        private readonly ILogger _logger;
        private readonly IRateLimitCounterStore _memoryCacheStore;
        private readonly RedisCache _redisCache;

        public RedisRateLimitCounterStore(
            IMemoryCache memoryCache,
            ILogger<RedisRateLimitCounterStore> logger)
        {
            _logger = logger;
            _memoryCacheStore = new MemoryCacheRateLimitCounterStore(memoryCache);

            _redisCache = new RedisCache();
        }

        public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            return await TryRedisCommandAsync(
                () =>
                {
                    return _redisCache.KeyExistsAsync(id, 0);
                },
                () =>
                {
                    return _memoryCacheStore.ExistsAsync(id, cancellationToken);
                });
        }

        public async Task<RateLimitCounter?> GetAsync(string id, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            return await TryRedisCommandAsync(
                async () =>
                {
                    var value = await _redisCache.GetStringAsync(id, 0);

                    if (!string.IsNullOrEmpty(value))
                    {
                        return JsonConvert.DeserializeObject<RateLimitCounter?>(value);
                    }

                    return null;
                },
                () =>
                {
                    return _memoryCacheStore.GetAsync(id, cancellationToken);
                });
        }

        public async Task RemoveAsync(string id, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            _ = await TryRedisCommandAsync(
                async () =>
                {
                    await _redisCache.KeyDeleteAsync(id, 0);

                    return true;
                },
                async () =>
                {
                    await _memoryCacheStore.RemoveAsync(id, cancellationToken);

                    return true;
                });
        }

        public async Task SetAsync(string id, RateLimitCounter? entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            _ = await TryRedisCommandAsync(
                async () =>
                {
                    var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1;
                    await _redisCache.SetStringAsync(id, JsonConvert.SerializeObject(entry.Value), exprie);

                    return true;
                },
                async () =>
                {
                    await _memoryCacheStore.SetAsync(id, entry, expirationTime, cancellationToken);

                    return true;
                });
        }

        private async Task<T> TryRedisCommandAsync<T>(Func<Task<T>> command, Func<Task<T>> fallbackCommand)
        {
            if (_redisCache != null)
            {
                try
                {
                    return await command();
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Redis command failed: {ex}");
                }
            }

            return await fallbackCommand();
        }
    }

 2.ip 특수 규칙

public class RedisIpPolicyStore : IIpPolicyStore
    {
        private readonly IpRateLimitOptions _options;
        private readonly IpRateLimitPolicies _policies;
        private readonly RedisCache _redisCache;
        public RedisIpPolicyStore(
            IOptions<IpRateLimitOptions> options = null,
            IOptions<IpRateLimitPolicies> policies = null)
        {
            _options = options?.Value;
            _policies = policies?.Value;
            _redisCache = new RedisCache();
        }

        public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)
        {
            return await _redisCache.KeyExistsAsync($"{_options.IpPolicyPrefix}", 0);
        }

        public async Task<IpRateLimitPolicies> GetAsync(string id, CancellationToken cancellationToken = default)
        {
            string stored = await _redisCache.GetStringAsync($"{_options.IpPolicyPrefix}", 0);
            if (!string.IsNullOrEmpty(stored))
            {
                return JsonConvert.DeserializeObject<IpRateLimitPolicies>(stored);
            }

            return default;
        }

        public async Task RemoveAsync(string id, CancellationToken cancellationToken = default)
        {
            await _redisCache.DelStringAsync($"{_options.IpPolicyPrefix}", 0);
        }

        public async Task SeedAsync()
        {
            // on startup, save the IP rules defined in appsettings
            if (_options != null && _policies != null)
            {
                await _redisCache.SetStringAsync($"{_options.IpPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0).ConfigureAwait(false);
            }
        }

        public async Task SetAsync(string id, IpRateLimitPolicies entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default)
        {
            var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1;
            await _redisCache.SetStringAsync($"{_options.IpPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0, exprie);
        }
    }

3.클 라 이언 트 특수 규칙

public class RedisClientPolicyStore : IClientPolicyStore
    {
        private readonly ClientRateLimitOptions _options;
        private readonly ClientRateLimitPolicies _policies;
        private readonly RedisCache _redisCache;
        public RedisClientPolicyStore(
            IOptions<ClientRateLimitOptions> options = null,
            IOptions<ClientRateLimitPolicies> policies = null)
        {
            _options = options?.Value;
            _policies = policies?.Value;
            _redisCache = new RedisCache();
        }

        public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)
        {
            return await _redisCache.KeyExistsAsync($"{_options.ClientPolicyPrefix}", 0);
        }

        public async Task<ClientRateLimitPolicy> GetAsync(string id, CancellationToken cancellationToken = default)
        {
            string stored = await _redisCache.GetStringAsync($"{_options.ClientPolicyPrefix}", 0);
            if (!string.IsNullOrEmpty(stored))
            {
                return JsonConvert.DeserializeObject<ClientRateLimitPolicy>(stored);
            }

            return default;
        }

        public async Task RemoveAsync(string id, CancellationToken cancellationToken = default)
        {
            await _redisCache.DelStringAsync($"{_options.ClientPolicyPrefix}", 0);
        }

        public async Task SeedAsync()
        {
            // on startup, save the IP rules defined in appsettings
            if (_options != null && _policies != null)
            {
                await _redisCache.SetStringAsync($"{_options.ClientPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0).ConfigureAwait(false);
            }
        }

        public async Task SetAsync(string id, ClientRateLimitPolicy entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default)
        {
            var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1;
            await _redisCache.SetStringAsync($"{_options.ClientPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0, exprie);
        }
    }

이후 Startup 파일 에 대응 하 는 주입 을 추가 합 니 다.

services.AddSingleton<IRateLimitCounterStore, RedisRateLimitCounterStore>();
services.AddSingleton<IIpPolicyStore, RedisIpPolicyStore>();
services.AddSingleton<IClientPolicyStore, RedisClientPolicyStore>();


그리고 실행 하면 redis 에서 볼 수 있어 요.

 5.규칙 수정
규칙 은 IP 와 클 라 이언 트 의 특수 한 규칙 만 수정 할 수 있 습 니 다.이전 부분 에 규칙 을 바 꾸 는 대응 redis 추가 삭제 검사 기능 이 주입 되 었 기 때문에 우 리 는 이런 방법 으로 규칙 을 다시 쓸 수 있 습 니 다.다음 과 같 습 니 다.

public class ClientRateLimitController : Controller
{
    private readonly ClientRateLimitOptions _options;
    private readonly IClientPolicyStore _clientPolicyStore;

    public ClientRateLimitController(IOptions<ClientRateLimitOptions> optionsAccessor, IClientPolicyStore clientPolicyStore)
    {
        _options = optionsAccessor.Value;
        _clientPolicyStore = clientPolicyStore;
    }

    [HttpGet]
    public ClientRateLimitPolicy Get()
    {
        return _clientPolicyStore.Get($"{_options.ClientPolicyPrefix}_cl-key-1");
    }

    [HttpPost]
    public void Post()
    {
        var id = $"{_options.ClientPolicyPrefix}_cl-key-1";
        var clPolicy = _clientPolicyStore.Get(id);
        clPolicy.Rules.Add(new RateLimitRule
        {
            Endpoint = "*/api/testpolicyupdate",
            Period = "1h",
            Limit = 100
        });
        _clientPolicyStore.Set(id, clPolicy);
    }
}
여기 서'Net Core 제한 흐름 의 실현 예시'에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 관련 내용 을 소개 합 니 다.Net Core 제한 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

좋은 웹페이지 즐겨찾기