网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using Dpz.Core.Public.Entity;
using Wangkanai.Detection.Models;
using Wangkanai.Detection.Services;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace Dpz.Core.Web.Library.Middleware;

public class RejectBots(
    RequestDelegate next,
    IConfiguration configuration,
    IWebHostEnvironment webHostEnvironment
)
{
    private readonly Dictionary<string, object> _attributes = new();

    public async Task Invoke(
        HttpContext httpContext,
        ILoggerFactory loggerFactory,
        IBlockService blockService,
        IDetectionService detectionService
    )
    {
        var logger = loggerFactory.CreateLogger<RejectBots>();
        var requestIpAddress = httpContext.Request.GetIpAddress();
        List<VmBlock> blocks = [];
        try
        {
            blocks = await blockService.GetBlocksAsync();
        }
        catch (Exception e)
        {
            logger.LogError(e, "get blocks fail");
        }

        if (
            (
                detectionService.Browser.Name is Browser.Chrome or Browser.Firefox
                && detectionService.Browser.Version < new Version("100.0.0.0")
            )
            || detectionService.Browser.Name is Browser.InternetExplorer
        )
        {
            await Response403Async(httpContext, logger, requestIpAddress);
            return;
        }

        var bots = configuration.GetSection("RejectBots").Get<List<RejectBot>>() ?? [];
        var ignoreRequestPrefixes =
            configuration.GetSection("IgnoreRequestPrefix").Get<List<string>>() ?? [];
        if (IsBot(httpContext, bots, requestIpAddress, blocks))
        {
            await Response403Async(httpContext, logger, requestIpAddress);
            return;
        }

        await next.Invoke(httpContext);

        await Record404NotFoundAsync(blockService, httpContext, ignoreRequestPrefixes, blocks);
    }

    private bool IsBot(
        HttpContext httpContext,
        List<RejectBot> bots,
        string requestIpAddress,
        List<VmBlock> blocks
    )
    {
        var blockAccessThreshold = configuration.GetValue("BlockAccessThreshold", 0);

        var currentBlock = blocks.Find(x =>
            x.AccessCount >= blockAccessThreshold
            && (
                x.UserAgents.Contains(
                    httpContext.Request.Headers.UserAgent.ToString(),
                    new IgnoreCase()
                )
                || x.IpAddresses.Contains(requestIpAddress, new IgnoreCase())
                || string.Equals(httpContext.Request.Path, x.RequestPath)
            )
        );
        if (currentBlock != null)
        {
            _attributes.TryAdd("@Block", currentBlock);
            return true;
        }

        return bots.Any(x =>
        {
            if (!string.IsNullOrEmpty(x.UserAgent) && string.IsNullOrEmpty(x.IpAddress))
            {
                return httpContext.Request.Headers.UserAgent == x.UserAgent;
            }

            if (string.IsNullOrEmpty(x.UserAgent) && !string.IsNullOrEmpty(x.IpAddress))
            {
                return requestIpAddress == x.IpAddress;
            }

            if (!string.IsNullOrEmpty(x.UserAgent) && !string.IsNullOrEmpty(x.IpAddress))
            {
                return httpContext.Request.Headers.UserAgent == x.UserAgent
                    && requestIpAddress == x.IpAddress;
            }

            return false;
        });
    }

    private async Task Record404NotFoundAsync(
        IBlockService blockService,
        HttpContext httpContext,
        List<string> ignoreRequestPrefixes,
        List<VmBlock> blocks
    )
    {
        if (
            httpContext.Response.StatusCode == 404
            && !ignoreRequestPrefixes.Any(x =>
                httpContext
                    .Request.Path.ToString()
                    .StartsWith(x, StringComparison.OrdinalIgnoreCase)
            )
        )
        {
            var ipAddress = httpContext
                .Request.GetIpAddress()
                .Split(',')
                .Where(x => !string.IsNullOrEmpty(x))
                .Select(x => x.Trim())
                .ToArray();
            var userAgent = httpContext.Request.Headers.UserAgent;
            var method = httpContext.Request.Method.ToUpper();
            var requestPath = httpContext.Request.Path;

            if (
                blocks.Any(x =>
                    string.Equals(x.RequestMethod, method, StringComparison.OrdinalIgnoreCase)
                    && string.Equals(x.RequestPath, requestPath, StringComparison.OrdinalIgnoreCase)
                    && x.UserAgents.Any(y =>
                        string.Equals(y, userAgent, StringComparison.OrdinalIgnoreCase)
                    )
                    && x.IpAddresses.Except(ipAddress).Any()
                )
            )
            {
                await blockService.IncrementCountAsync(method, requestPath);
                return;
            }

            var block = new Block
            {
                RequestMethod = method,
                RequestPath = requestPath,
                IpAddresses = ipAddress,
                UserAgents = userAgent
            };
            await blockService.SaveBlockAsync(block);
        }
    }

    private async Task Response403Async(HttpContext httpContext, ILogger logger, string ipAddress)
    {
        //var logger = loggerFactory.CreateLogger("Dpz.Core.Web.Library.Middleware.RejectBots");
        httpContext.Response.StatusCode = 403;
        httpContext.Response.ContentType = "text/html";
        await httpContext.Response.SendFileAsync(
            Path.Combine(webHostEnvironment.WebRootPath, "reject.html")
        );

        var tags = new Dictionary<string, object>
        {
            { "IpAddress", ipAddress },
            { "UserAgent", httpContext.Request.Headers.UserAgent },
            { "RequestHost", httpContext.Request.Host },
            { "RequestScheme", httpContext.Request.Scheme },
            { "RequestMethod", httpContext.Request.Method },
            { "RequestPath", httpContext.Request.Path },
            { "RequestQueryString", httpContext.Request.QueryString },
        };
        foreach (var item in _attributes)
        {
            tags.TryAdd(item.Key, item.Value);
        }

        using (logger.BeginScope(tags))
        {
            logger.LogInformation("被拦截机器人");
        }
    }

    private class RejectBot
    {
        public string? UserAgent { get; set; }

        public string? IpAddress { get; set; }
    }

    private class IgnoreCase : IEqualityComparer<string>
    {
        public bool Equals(string? x, string? y)
        {
            if (x is null && y is null)
                return true;
            if (x is null || y is null)
                return false;
            return string.Equals(x, y, StringComparison.CurrentCultureIgnoreCase);
        }

        public int GetHashCode(string obj)
        {
            var result = "IgnoreCase".Sum(x => x);
            return obj.ToLower().GetHashCode() + result;
        }
    }
}
loading