ASP .Net Core 拦截恶意访问 中间件

2021-05-26  乐帮网

netcore

本教程只是抛砖引玉,展示了如何利用中间件来拦截指定的用户。下面开始我们的程序。首先建立一个空的ASP .Net Core MVC程序 。使用默认配置就可以了,接着我们写一个中间件,实现的功能是:记录所有用户10秒内访问的时间点儿,如果在10秒内访问次数超过了10次那么我们就认为此用户可能 为程序 其并不真实的用户。当然这里只是描述成这样的功能,你可以根据自己的实际情况扩展,例如黑名单制度,例如限定访问的资源有什么特征时才启用此规则。除此外我们还利用IP来判断是否同一个用户。

新建一个中间件类FrequencyMiddleware,具体代码如下:

public class FrequencyMiddleware
    {
        private readonly RequestDelegate _next;

        private readonly ILogger<FrequencyMiddleware> _logger;

        private readonly IConfiguration _configuration;

        

        public FrequencyMiddleware(RequestDelegate next, ILogger<FrequencyMiddleware> logger, IConfiguration configuration)
        {
            _next = next;
            _logger = logger;
            _configuration = configuration;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {

                HttpRequest request = context.Request;
                string userIP;
                if (!string.IsNullOrEmpty(request.Headers["HTTP_X_FORWARDED_FOR"]))
                    userIP = request.Headers["REMOTE_ADDR"];
                else if (!string.IsNullOrEmpty(request.Headers["HTTP_X_FORWARDED_FOR"]))
                    userIP = request.Headers["HTTP_X_FORWARDED_FOR"];
                else if (!string.IsNullOrEmpty(request.Headers["X-Real-IP"]))
                    userIP = request.Headers["X-Real-IP"];
                else
                    userIP = request.HttpContext.Connection.RemoteIpAddress.ToString();

                if (string.IsNullOrEmpty(userIP))//|| !userIP.Contains(".")
                {
                    await _next.Invoke(context);
                    return;
                }

                string url = string.Concat(request.Scheme, "://", request.Host, request.Path, request.QueryString);
                string path = request.Path.ToString().ToLower();
                NoteModel blockModel = CacheHelper.GetCache<NoteModel>(userIP);

                if (blockModel == null)
                    blockModel = new NoteModel();
                blockModel.StepUp();
                CacheHelper.SetCache(userIP, blockModel, 10);

                if (blockModel.Count() >= 10)
                {

                    await context.Response.WriteAsync("Unauthorized access");
                    return;
                }

                //var features = context.Features;
            }
            catch (Exception e)
            {
                _logger.LogError(e, "FrequencyMiddleware Error ");
            }
            await _next.Invoke(context);
        }
    }

其中用到我自定义实体类NoteModel,代码如下 :

public class NoteModel
    {
        private IList<DateTime> Records { get; set; }

        private readonly int MAXCOUNT = 10;

        public int Count()
        {
            int res = 0;
            foreach (var d in Records)
            {
                if (d > DateTime.Now.AddSeconds(-MAXCOUNT))
                {
                    res++;
                }
            }
            return res;
        }

        public void StepUp()
        {
            if (Records == null)
            {
                Records = new List<DateTime>();

            }
            Records.Add(DateTime.Now); ;
        }
    }

还用到了Cach类,我封装如下:

public class CacheHelper
    {
        private static readonly Object locker = new object();

        private static MemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions() { });
        /// <summary>  
        /// 获取数据缓存  
        /// </summary>  
        /// <param name="key">键</param>  
        public static T GetCache<T>(string key)
        {
            T t = memoryCache.Get<T>(key);
            return t;
        }

        public static bool IsExists(string key)
        {
            object res;
            memoryCache.TryGetValue(key, out res);
            if (res == null)
                return false;
            else
                return true;
        }
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <typeparam name="T">T</typeparam>
        /// <param name="key">键</param>
        /// <param name="cachePopulate">定义获取</param>
        /// <param name="slidingExpiration">过期时间</param>
        /// <param name="absoluteExpiration">过期时间</param>
        /// <returns>T</returns>
        public static T GetCache<T>(string key, Func<T> cachePopulate, TimeSpan? slidingExpiration = null, DateTimeOffset? absoluteExpiration = null)
        {
            if (String.IsNullOrWhiteSpace(key)) throw new ArgumentException("Invalid cache key");
            if (cachePopulate == null) throw new ArgumentNullException("cachePopulate");
            if (slidingExpiration == null && absoluteExpiration == null) throw new ArgumentException("Either a sliding expiration or absolute must be provided");
            T res;
            memoryCache.TryGetValue<T>(key, out res);
            if (res == null)
            {
                lock (locker)
                {
                    memoryCache.TryGetValue<T>(key, out res);
                    if (res == null)
                    {
                        res = cachePopulate();
                        if (slidingExpiration != null)
                            memoryCache.Set<T>(key, res, slidingExpiration.Value);
                        else if (absoluteExpiration != null)
                            memoryCache.Set<T>(key, res, absoluteExpiration.Value);
                    }
                }
            }

            return res;
        }

        /// <summary>
        /// 设置数据缓存
        /// </summary>
        /// <param name="cacheKey">cacheKey</param>
        /// <param name="objObject">objObject</param>
        /// <param name="timeout">Seconds</param>
        public static void SetCache(string cacheKey, object objObject, int timeout = 7200)
        {
            try
            {
                if (objObject == null) return;
                memoryCache.Set(cacheKey, objObject, TimeSpan.FromSeconds(timeout));
            }
            catch (Exception)
            {
                //throw;  
            }
        }
        /// <summary>  
        /// 移除指定数据缓存  
        /// </summary>  
        public static void RemoveAllCache(string cacheKey)
        {
            memoryCache.Remove(cacheKey);
        }

    }

还有就是中间件的扩展方法,我定义如下:

 public static class FrequencyExtensions
    {
        /// <summary>
        /// Demo 中间件 lebang2020.cn
        /// </summary>
        /// <param name="builder">builder</param>
        /// <returns>IApplicationBuilder</returns>
        public static IApplicationBuilder UseFrequencyMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<FrequencyMiddleware>();
        }
    }

最后就是启用中间件了,我们在Startup配置启用中间件

app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseFrequencyMiddleware();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

我们可以按F5进行测试,然后就是在浏览器连接刷新页面10秒内刷新多次。最终会看到我们的访问被 禁止了,而过了30秒后又会自动恢复正常访问如下图。
001

这里提供百度网盘源码链接:https://pan.baidu.com/s/1rxpleoytLuc-yX78d1miUA 

公众号二维码

关注我的微信公众号
在公众号里留言交流
投稿邮箱:xinsiyu6@gmail.com

庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。

欧阳修

付款二维码

如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力