新增速率限制,调整本地文件上传,服务监控IP取本机网络IP,仓储增加方法

This commit is contained in:
2023-09-22 15:46:43 +08:00
parent d3556cc058
commit ee9f6bacf3
24 changed files with 822 additions and 24 deletions
@@ -0,0 +1,47 @@
using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
using RateLimitRule = AspNetCoreRateLimit.RateLimitRule;
namespace ZR.ServiceCore.Middleware;
public class CustomIpRateLimitMiddleware : IpRateLimitMiddleware
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IIpRateLimitLogService _ipRateLimitLogService;
public CustomIpRateLimitMiddleware(RequestDelegate next, IProcessingStrategy processingStrategy,
IOptions<IpRateLimitOptions> options, IIpPolicyStore policyStore, IRateLimitConfiguration config,
ILogger<IpRateLimitMiddleware> logger, IIpRateLimitLogService ipRateLimitLogService) : base(next, processingStrategy, options, policyStore, config, logger)
{
_ipRateLimitLogService = ipRateLimitLogService;
}
protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity,
RateLimitCounter counter, RateLimitRule rule)
{
// base.LogBlockedRequest(httpContext, identity, counter, rule);
var nowDate = DateTime.Now;
var ipRateLimitLog = new IpRateLimitLog
{
HttpVerb = identity.HttpVerb,
Path = identity.Path,
ClientIp = identity.ClientIp,
Limit = rule.Limit,
Period = rule.Period,
Exceeded = counter.Count - rule.Limit,
Endpoint = rule.Endpoint,
CreateTime = nowDate
};
var logStr = $"请求 {ipRateLimitLog.HttpVerb}:{ipRateLimitLog.Path} 来自 IP {ipRateLimitLog.ClientIp} 已被阻止, " +
$"配额 {ipRateLimitLog.Limit}/{ipRateLimitLog.Period} 超出次数 {ipRateLimitLog.Exceeded}. " +
$"被规则 {ipRateLimitLog.Endpoint} 阻止. 时间: {ipRateLimitLog.CreateTime}";
Logger.Info(logStr);
_ipRateLimitLogService.InsertIpRateLimitLogAsync(ipRateLimitLog);
}
}
+26
View File
@@ -0,0 +1,26 @@
namespace ZR.ServiceCore.Model;
[SugarTable("ip_rate_limit_log")]
[Tenant("0")]
public class IpRateLimitLog
{
[SugarColumn(IsPrimaryKey = true)]
[JsonConverter(typeof(ValueToStringConverter))]
public long Id { get; set;}
public string HttpVerb { get; set; }
public string Path { get; set; }
public string ClientIp { get; set; }
public double Limit { get; set; }
public string Period { get; set; }
public double Exceeded { get; set; }
public string Endpoint { get; set; }
public DateTime CreateTime { get; set; }
}
+17
View File
@@ -0,0 +1,17 @@
namespace ZR.ServiceCore.Model;
[SugarTable("ip_rate_limit_policy")]
[Tenant("0")]
public class IpRateLimitPolicy
{
[SugarColumn(IsPrimaryKey = true)]
[JsonConverter(typeof(ValueToStringConverter))]
public long Id { get; set; }
public string Ip { get; set; }
[Navigate(NavigateType.OneToMany, nameof(RateLimitRule.IpRateLimitPolicyId))]
public List<RateLimitRule> Rules { get; set; }
public char Flag { get; set; } = '0';
}
+21
View File
@@ -0,0 +1,21 @@
namespace ZR.ServiceCore.Model;
[SugarTable("rate_limit_rule")]
[Tenant("0")]
public class RateLimitRule
{
[SugarColumn(IsPrimaryKey = true)]
[JsonConverter(typeof(ValueToStringConverter))]
public long Id { get; set; }
[JsonConverter(typeof(ValueToStringConverter))]
public long IpRateLimitPolicyId { get; set; }
public string Endpoint { get; set; }
public string Period { get; set; }
public double Limit { get; set; }
public char Flag { get; set; } = '0';
}
+20
View File
@@ -0,0 +1,20 @@
namespace ZR.ServiceCore.Model;
[SugarTable("sys_login_limit")]
[Tenant("0")]
public class SysLoginLimit
{
[SugarColumn(IsPrimaryKey = true)]
[JsonConverter(typeof(ValueToStringConverter))]
public long Id { get; set; }
public string UserName { get; set; }
public int? ErrorCount { get; set; }
public DateTime? ErrorTime { get; set; }
public string Ip { get; set; }
public char Flag { get; set; }
}
@@ -0,0 +1,12 @@
using ZR.Model;
using ZR.Service;
using ZR.ServiceCore.Model;
namespace ZR.ServiceCore.Services.IService;
public interface IIpRateLimitLogService : IBaseService<IpRateLimitLog>
{
void InsertIpRateLimitLogAsync(IpRateLimitLog ipRateLimitLog);
Task<PagedInfo<IpRateLimitLog>> SelectIpRateLimitLogPageAsync(IpRateLimitLog ipRateLimitLog, PagerInfo pager);
}
@@ -0,0 +1,12 @@
using ZR.Model;
using ZR.Service;
using ZR.ServiceCore.Model;
namespace ZR.ServiceCore.Services.IService;
public interface IIpRateLimitPolicyService : IBaseService<IpRateLimitPolicy>
{
Task<PagedInfo<IpRateLimitPolicy>> SelectIpRateLimitPolicyPageAsync(IpRateLimitPolicy ipRateLimitPolicy,
PagerInfo pager);
}
@@ -0,0 +1,11 @@
using ZR.Service;
using ZR.ServiceCore.Model;
namespace ZR.ServiceCore.Services.IService;
public interface IRateLimitRuleService : IBaseService<RateLimitRule>
{
Task<List<RateLimitRule>> DeleteRateLimitRuleAsync(long id);
Task<char> ChangeRateLimitRuleFlagAsync(long id);
}
@@ -19,7 +19,7 @@ namespace ZR.Service.System.IService
/// <param name="rootPath"></param>
/// <param name="userName"></param>
/// <returns>文件对象</returns>
Task<SysFile> SaveFileToLocal(string rootPath, string fileName, string fileDir, string userName, IFormFile formFile);
Task<SysFile> SaveFileToLocal(string rootPath, string fileName, string fileDir, string userName, IFormFile formFile, string uploadUrl);
Task<SysFile> SaveFileToAliyun(SysFile file, IFormFile formFile);
/// <summary>
@@ -0,0 +1,11 @@
using ZR.Service;
using ZR.ServiceCore.Model;
namespace ZR.ServiceCore.Services.IService;
public interface ISysLoginLimitService : IBaseService<SysLoginLimit>
{
Task<bool> AddSysLoginLimitAsync(SysLoginLimit sysLoginLimit);
Task<bool> RemoveSysLoginLimitAsync(SysLoginLimit sysLoginLimit);
}
@@ -0,0 +1,31 @@
using Infrastructure.Attribute;
using ZR.Model;
using ZR.Service;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
namespace ZR.ServiceCore.Services;
[AppService(ServiceType = typeof(IIpRateLimitLogService), ServiceLifetime = LifeTime.Transient)]
public class IpRateLimitLogService : BaseService<IpRateLimitLog>, IIpRateLimitLogService
{
public void InsertIpRateLimitLogAsync(IpRateLimitLog ipRateLimitLog)
{
Insertable(ipRateLimitLog).ExecuteReturnSnowflakeId();
}
public async Task<PagedInfo<IpRateLimitLog>> SelectIpRateLimitLogPageAsync(IpRateLimitLog ipRateLimitLog, PagerInfo pager)
{
RefAsync<int> total = 0;
var res = await Queryable()
.ToPageListAsync(pager.PageNum, pager.PageSize, total);
var page = new PagedInfo<IpRateLimitLog>
{
PageSize = pager.PageSize,
PageIndex = pager.PageNum,
Result = res,
TotalNum = total
};
return page;
}
}
@@ -0,0 +1,27 @@
using Infrastructure.Attribute;
using ZR.Model;
using ZR.Service;
using ZR.ServiceCore.Services.IService;
using IpRateLimitPolicy = ZR.ServiceCore.Model.IpRateLimitPolicy;
namespace ZR.ServiceCore.Services;
[AppService(ServiceType = typeof(IIpRateLimitPolicyService), ServiceLifetime = LifeTime.Transient)]
public class IpRateLimitPolicyService : BaseService<IpRateLimitPolicy>, IIpRateLimitPolicyService
{
public async Task<PagedInfo<IpRateLimitPolicy>> SelectIpRateLimitPolicyPageAsync(IpRateLimitPolicy ipRateLimitPolicy, PagerInfo pager)
{
RefAsync<int> total = 0;
var res = await Queryable()
.Includes(it => it.Rules)
.ToPageListAsync(pager.PageNum, pager.PageSize, total);
var page = new PagedInfo<IpRateLimitPolicy>
{
PageSize = pager.PageSize,
PageIndex = pager.PageNum,
Result = res,
TotalNum = total
};
return page;
}
}
@@ -0,0 +1,51 @@
using Infrastructure;
using Infrastructure.Attribute;
using ZR.Service;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
namespace ZR.ServiceCore.Services;
[AppService(ServiceType = typeof(IRateLimitRuleService), ServiceLifetime = LifeTime.Transient)]
public class RateLimitRuleService : BaseService<RateLimitRule>, IRateLimitRuleService
{
public async Task<List<RateLimitRule>> DeleteRateLimitRuleAsync(long id)
{
var isExist = await Queryable()
.Where(it => it.Id == id)
.SingleAsync();
if (isExist == null)
{
throw new CustomException("数据不存在,无法删除");
}
var del = await Deleteable()
.Where(it => it.Id == id)
.ExecuteCommandAsync();
if (del <= 0) throw new CustomException("删除失败");
{
var res = await Queryable()
.Where(it => it.IpRateLimitPolicyId == isExist.IpRateLimitPolicyId)
.ToListAsync();
return res;
}
}
public async Task<char> ChangeRateLimitRuleFlagAsync(long id)
{
var isExist = await Queryable()
.Where(it => it.Id == id)
.SingleAsync();
if (isExist == null) throw new CustomException("数据不存在,无法更改");
var upd = await Updateable(new RateLimitRule
{
Id = id,
Flag = isExist.Flag == '1' ? '0' : '1'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
if (upd == 0) throw new CustomException("更改失败");
{
var res = isExist.Flag == '1' ? '0' : '1';
return res;
}
}
}
+2 -2
View File
@@ -37,7 +37,7 @@ namespace ZR.Service.System
/// <param name="formFile">上传的文件流</param>
/// <param name="userName"></param>
/// <returns></returns>
public async Task<SysFile> SaveFileToLocal(string rootPath, string fileName, string fileDir, string userName, IFormFile formFile)
public async Task<SysFile> SaveFileToLocal(string rootPath, string fileName, string fileDir, string userName, IFormFile formFile, string uploadUrl)
{
string fileExt = Path.GetExtension(formFile.FileName);
fileName = (fileName.IsEmpty() ? HashFileName() : fileName) + fileExt;
@@ -55,7 +55,7 @@ namespace ZR.Service.System
{
await formFile.CopyToAsync(stream);
}
string uploadUrl = OptionsSetting.Upload.UploadUrl;
// string uploadUrl = OptionsSetting.Upload.UploadUrl;
string accessPath = string.Concat(uploadUrl, "/", filePath.Replace("\\", "/"), "/", fileName);
SysFile file = new(formFile.FileName, fileName, fileExt, fileSize + "kb", filePath, userName)
{
@@ -0,0 +1,194 @@
using AspNetCoreRateLimit;
using Infrastructure;
using Infrastructure.Attribute;
using Mapster;
using Microsoft.Extensions.Options;
using ZR.Service;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
using IpRateLimitPolicy = ZR.ServiceCore.Model.IpRateLimitPolicy;
using RateLimitRule = ZR.ServiceCore.Model.RateLimitRule;
namespace ZR.ServiceCore.Services;
[AppService(ServiceType = typeof(ISysLoginLimitService), ServiceLifetime = LifeTime.Transient)]
public class SysLoginLimitService : BaseService<SysLoginLimit>, ISysLoginLimitService
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
private readonly IRateLimitRuleService _rateLimitRuleService;
private readonly IIpRateLimitPolicyService _ipRateLimitPolicyService;
public SysLoginLimitService(IIpPolicyStore ipPolicyStore, IOptions<IpRateLimitOptions> optionsAccessor,
IIpRateLimitPolicyService ipRateLimitPolicyService, IRateLimitRuleService rateLimitRuleService)
{
_ipPolicyStore = ipPolicyStore;
_ipRateLimitPolicyService = ipRateLimitPolicyService;
_rateLimitRuleService = rateLimitRuleService;
_options = optionsAccessor.Value;
}
public async Task<bool> AddSysLoginLimitAsync(SysLoginLimit sysLoginLimit)
{
try
{
await Context.Ado.BeginTranAsync();
var rateLimitRule = new RateLimitRule
{
Endpoint = "*",
Period = "1s",
Limit = 0,
Flag = '1'
};
var ipRateLimitPolicy = new IpRateLimitPolicy
{
Ip = sysLoginLimit.Ip,
Flag = '1',
Rules = new List<RateLimitRule>
{
rateLimitRule
}
};
var existIpRateLimitPolicy = await _ipRateLimitPolicyService.Queryable()
.Where(it => it.Ip == sysLoginLimit.Ip)
.FirstAsync();
if (existIpRateLimitPolicy != null)
{
rateLimitRule.IpRateLimitPolicyId = existIpRateLimitPolicy.Id;
var rateLimitRuleStore = await _rateLimitRuleService
.Storageable(rateLimitRule)
.WhereColumns(it => new
{
it.IpRateLimitPolicyId,
it.Endpoint,
it.Period,
it.Limit
})
.ToStorageAsync();
await rateLimitRuleStore.AsInsertable.ExecuteReturnSnowflakeIdAsync();
await rateLimitRuleStore.AsUpdateable
.IgnoreColumns(it => it.Id)
.ExecuteCommandAsync();
if (existIpRateLimitPolicy.Flag != '1')
{
await _ipRateLimitPolicyService.Updateable(new IpRateLimitPolicy
{
Id = existIpRateLimitPolicy.Id,
Flag = '1'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
}
}
else
{
await _ipRateLimitPolicyService.InsertNav(ipRateLimitPolicy)
.Include(it => it.Rules)
.ExecuteCommandAsync();
}
var res = await _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(res.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>());
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
await Updateable(new SysLoginLimit
{
Id = sysLoginLimit.Id,
Flag = '1'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
await Context.Ado.CommitTranAsync();
return true;
}
catch (Exception e)
{
await Context.Ado.RollbackTranAsync();
var res = await _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(res.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>());
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
throw;
}
}
public async Task<bool> RemoveSysLoginLimitAsync(SysLoginLimit sysLoginLimit)
{
try
{
await Context.Ado.BeginTranAsync();
// 查询是否存在此IP的规则
var existIpRateLimitPolicy = await _ipRateLimitPolicyService.Queryable()
.Where(it => it.Ip == sysLoginLimit.Ip)
.FirstAsync();
if (existIpRateLimitPolicy != null)
{
var rateLimitRule = new RateLimitRule
{
Endpoint = "*",
Period = "1s",
Limit = 0,
Flag = '0',
IpRateLimitPolicyId = existIpRateLimitPolicy.Id
};
var rateLimitRuleStore = await _rateLimitRuleService
.Storageable(rateLimitRule)
.WhereColumns(it => new
{
it.IpRateLimitPolicyId,
it.Endpoint,
it.Period,
it.Limit
})
.ToStorageAsync();
await rateLimitRuleStore.AsInsertable.ExecuteReturnSnowflakeIdAsync();
await rateLimitRuleStore.AsUpdateable
.IgnoreColumns(it => it.Id)
.ExecuteCommandAsync();
var res = await _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(res.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>());
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
await Updateable(new SysLoginLimit
{
Id = sysLoginLimit.Id,
Flag = '0',
ErrorCount = 0
}).UpdateColumns(it => new { it.Flag, it.ErrorCount })
.ExecuteCommandAsync();
await Context.Ado.CommitTranAsync();
return true;
}
throw new CustomException("不存在此IP的规则");
}
catch (Exception e)
{
await Context.Ado.RollbackTranAsync();
var res = await _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(res.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>());
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
throw;
}
}
}