AspNetCoreRateLimit 学习

GitHub:https://github.com/stefanprodan/AspNetCoreRateLimit

Coding:https://huawu.coding.net/public/aspnetcoreratelimit/aspnetcoreratelimit/git/files

 

主要包含 IP 和 ClientID 两种规则

IP

优先获取请求头:X-Real-IP

其次获取:_httpContextAccessor.HttpContext.Connection.RemoteIpAddress?.ToString()

 

ClientID 

使用配置中 ClientIdHeader 定义的请求头 

如:ClientIdHeader:X-ClientId,则请求头为:X-ClientId

 

普通规则 GeneralRules 用法

1.当启用 EnableEndpointRateLimiting 时,会匹配 普通规则中 * 或 *:/matching_path 或 matching_verb:/matching_path  会对比一个 IsUrlMatch 方法

var limits = new List<RateLimitRule>();

//rules 为对应的IP或ClientID中定义的Rules if (rules?.Any() == true) { if (_options.EnableEndpointRateLimiting) { // search for rules with endpoints like "*" and "*:/matching_path" string path = _options.EnableRegexRuleMatching ? $".+:{identity.Path}" : $"*:{identity.Path}"; var pathLimits = rules.Where(r => path.IsUrlMatch(r.Endpoint, _options.EnableRegexRuleMatching)); limits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" var verbLimits = rules.Where(r => $"{identity.HttpVerb}:{identity.Path}".IsUrlMatch(r.Endpoint, _options.EnableRegexRuleMatching)); limits.AddRange(verbLimits); } else { // ignore endpoint rules and search for global rules only var genericLimits = rules.Where(r => r.Endpoint == "*"); limits.AddRange(genericLimits); } // get the most restrictive limit for each period limits = limits.GroupBy(l => l.Period).Select(l => l.OrderBy(x => x.Limit)).Select(l => l.First()).ToList(); }

2.否则 精确匹配 普通规则中 EndPoint 等于 * 

 

IP和ClientID中的Rules

所有的通用规则和自定义规则都会生效,精确匹配的Endpoint 最好设置的规则比 *规则范围小
如果有和通用规则 Period 相等的规则,则优先使用此处的

 

public static bool IsUrlMatch(this string source, string value, bool useRegex)
{
    if (useRegex)
    {
        return IsRegexMatch(source, value);
    }
    return source.IsWildCardMatch(value);
}

public static bool IsWildCardMatch(this string source, string value)
{
    return source != null && value != null && source.ToLowerInvariant().IsMatch(value.ToLowerInvariant());
}

public static bool IsMatch(this string value, string pattern, char singleWildcard = '?', char multipleWildcard = '*')
{

    int[] inputPosStack = new int[(value.Length + 1) * (pattern.Length + 1)];   // Stack containing input positions that should be tested for further matching
    int[] patternPosStack = new int[inputPosStack.Length];                      // Stack containing pattern positions that should be tested for further matching
    int stackPos = -1;                                                          // Points to last occupied entry in stack; -1 indicates that stack is empty
    bool[,] pointTested = new bool[value.Length + 1, pattern.Length + 1];       // Each true value indicates that input position vs. pattern position has been tested

    int inputPos = 0;   // Position in input matched up to the first multiple wildcard in pattern
    int patternPos = 0; // Position in pattern matched up to the first multiple wildcard in pattern

    //if (pattern == null)
    //    pattern = string.Empty;

    // Match beginning of the string until first multiple wildcard in pattern
    while (inputPos < value.Length && patternPos < pattern.Length && pattern[patternPos] != multipleWildcard && (value[inputPos] == pattern[patternPos] || pattern[patternPos] == singleWildcard))
    {
        inputPos++;
        patternPos++;
    }

    // Push this position to stack if it points to end of pattern or to a general wildcard character
    if (patternPos == pattern.Length || pattern[patternPos] == multipleWildcard)
    {
        pointTested[inputPos, patternPos] = true;
        inputPosStack[++stackPos] = inputPos;
        patternPosStack[stackPos] = patternPos;
    }

    bool matched = false;

    // Repeat matching until either string is matched against the pattern or no more parts remain on stack to test
    while (stackPos >= 0 && !matched)
    {
        inputPos = inputPosStack[stackPos];         // Pop input and pattern positions from stack
        patternPos = patternPosStack[stackPos--];   // Matching will succeed if rest of the input string matches rest of the pattern

        if (inputPos == value.Length && patternPos == pattern.Length)
            matched = true;     // Reached end of both pattern and input string, hence matching is successful
        else if (patternPos == pattern.Length - 1)
            matched = true;     // Current pattern character is multiple wildcard and it will match all the remaining characters in the input string
        else
        {
            // First character in next pattern block is guaranteed to be multiple wildcard
            // So skip it and search for all matches in value string until next multiple wildcard character is reached in pattern

            for (int curInputStart = inputPos; curInputStart < value.Length; curInputStart++)
            {

                int curInputPos = curInputStart;
                int curPatternPos = patternPos + 1;

                while (curInputPos < value.Length && curPatternPos < pattern.Length && pattern[curPatternPos] != multipleWildcard &&
                       (value[curInputPos] == pattern[curPatternPos] || pattern[curPatternPos] == singleWildcard))
                {
                    curInputPos++;
                    curPatternPos++;
                }

                // If we have reached next multiple wildcard character in pattern without breaking the matching sequence, then we have another candidate for full match
                // This candidate should be pushed to stack for further processing
                // At the same time, pair (input position, pattern position) will be marked as tested, so that it will not be pushed to stack later again
                if (((curPatternPos == pattern.Length && curInputPos == value.Length) || (curPatternPos < pattern.Length && pattern[curPatternPos] == multipleWildcard))
                    && !pointTested[curInputPos, curPatternPos])
                {
                    pointTested[curInputPos, curPatternPos] = true;
                    inputPosStack[++stackPos] = curInputPos;
                    patternPosStack[stackPos] = curPatternPos;
                }
            }
        }
    }

    return matched;
}

 

动态修改规则

在Main 方法中添加如下代码

        var host=hostBuilder.Build()

            #region RateLimit 很关键,动态修改规则
            using (var scope = host.Services.CreateScope())
            {
                // get the ClientPolicyStore instance
                var clientPolicyStore = scope.ServiceProvider.GetRequiredService<IClientPolicyStore>();
                // seed Client data from appsettings
                _ = clientPolicyStore.SeedAsync();

                // get the IpPolicyStore instance
                var ipPolicyStore = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();
                // seed IP data from appsettings
                _ = ipPolicyStore.SeedAsync();
            }
            #endregion

            host.Run();    

  

 

posted @ 2020-08-24 17:45  我的用户名  阅读(933)  评论(0编辑  收藏  举报