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

This commit is contained in:
YUN-PC5\user 2023-09-22 15:46:43 +08:00
parent d3556cc058
commit ee9f6bacf3
24 changed files with 822 additions and 24 deletions

View File

@ -16,8 +16,10 @@ namespace Infrastructure
types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null); types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("AddTime", flag)?.SetValue(source, DateTime.Now, null); types.GetProperty("AddTime", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("CreateBy", flag)?.SetValue(source, context.GetName(), null); types.GetProperty("CreateBy", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("Create_by", flag)?.SetValue(source, context.GetName(), null); types.GetProperty("CreateName", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("Create_by", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("Create_name", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("UserId", flag)?.SetValue(source, context.GetUId(), null); types.GetProperty("UserId", flag)?.SetValue(source, context.GetUId(), null);
return source; return source;
@ -31,9 +33,10 @@ namespace Infrastructure
types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null); types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("Update_time", flag)?.SetValue(source, DateTime.Now, null); types.GetProperty("Update_time", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("UpdateBy", flag)?.SetValue(source, context.GetName(), null); types.GetProperty("UpdateBy", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("Update_by", flag)?.SetValue(source, context.GetName(), null); types.GetProperty("Update_by", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("UpdateName", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("Update_name", flag)?.SetValue(source, context.GetNickName(), null);
return source; return source;
} }

View File

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Mvc; using System.Net;
using System.Net.Sockets;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
@ -126,6 +128,15 @@ namespace ZR.Admin.WebApi.Controllers
SysFile file = new(); SysFile file = new();
string fileExt = Path.GetExtension(formFile.FileName);//文件后缀 string fileExt = Path.GetExtension(formFile.FileName);//文件后缀
double fileSize = Math.Round(formFile.Length / 1024.0, 2);//文件大小KB double fileSize = Math.Round(formFile.Length / 1024.0, 2);//文件大小KB
var scheme = HttpContext.Request.Scheme + "://";
var serverIP = HttpContext.Request.Host.Value;
if (WebHostEnvironment.IsProduction())
{
var host = await Dns.GetHostEntryAsync(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
}
if (OptionsSetting.Upload.NotAllowedExt.Contains(fileExt)) if (OptionsSetting.Upload.NotAllowedExt.Contains(fileExt))
{ {
@ -147,7 +158,8 @@ namespace ZR.Admin.WebApi.Controllers
{ {
uploadDto.FileDir = OptionsSetting.Upload.LocalSavePath; uploadDto.FileDir = OptionsSetting.Upload.LocalSavePath;
} }
file = await SysFileService.SaveFileToLocal(savePath, uploadDto.FileName, uploadDto.FileDir, HttpContext.GetName(), formFile); file = await SysFileService.SaveFileToLocal(savePath, uploadDto.FileName, uploadDto.FileDir,
HttpContext.GetName(), formFile, scheme + serverIP);
break; break;
case StoreType.REMOTE: case StoreType.REMOTE:
break; break;

View File

@ -1,3 +1,5 @@
using System.Net;
using System.Net.Sockets;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model.System; using ZR.Model.System;
@ -119,7 +121,18 @@ namespace ZR.Admin.WebApi.Controllers.System
long userId = HttpContext.GetUId(); long userId = HttpContext.GetUId();
if (formFile == null) throw new CustomException("请选择文件"); if (formFile == null) throw new CustomException("请选择文件");
SysFile file = await FileService.SaveFileToLocal(hostEnvironment.WebRootPath, "", "avatar", HttpContext.GetName(), formFile); var scheme = HttpContext.Request.Scheme + "://";
var serverIP = HttpContext.Request.Host.Value;
if (hostEnvironment.IsProduction())
{
var host = await Dns.GetHostEntryAsync(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
}
var file = await FileService.SaveFileToLocal(hostEnvironment.WebRootPath, "", "avatar",
HttpContext.GetName(), formFile, serverIP);
UserService.UpdatePhoto(new SysUser() { Avatar = file.AccessUrl, UserId = userId }); UserService.UpdatePhoto(new SysUser() { Avatar = file.AccessUrl, UserId = userId });
return SUCCESS(new { imgUrl = file.AccessUrl }); return SUCCESS(new { imgUrl = file.AccessUrl });

View File

@ -0,0 +1,195 @@
using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
using IpRateLimitPolicy = ZR.ServiceCore.Model.IpRateLimitPolicy;
namespace ZR.Admin.WebApi.Controllers.System.monitor;
[Verify]
[Route("ip/route/limit")]
public class IpRateLimitController : BaseController
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
private readonly IIpRateLimitPolicyService _ipRateLimitPolicyService;
private readonly IRateLimitRuleService _rateLimitRuleService;
private readonly IIpRateLimitLogService _ipRateLimitLogService;
public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore,
IIpRateLimitPolicyService ipRateLimitPolicyService, IRateLimitRuleService rateLimitRuleService,
IIpRateLimitLogService ipRateLimitLogService)
{
_options = optionsAccessor.Value;
_ipPolicyStore = ipPolicyStore;
_ipRateLimitPolicyService = ipRateLimitPolicyService;
_rateLimitRuleService = rateLimitRuleService;
_ipRateLimitLogService = ipRateLimitLogService;
}
/// <summary>
/// 获取限制规则
/// </summary>
/// <returns></returns>
[HttpGet("getIpRateLimitPolicyPage")]
public async Task<IActionResult> GetIpRateLimitPolicyPage([FromQuery] IpRateLimitPolicy ipRateLimitPolicy,
PagerInfo pager)
{
var page = await _ipRateLimitPolicyService.SelectIpRateLimitPolicyPageAsync(ipRateLimitPolicy, pager);
return SUCCESS(page);
}
[HttpPost("addIpRateLimitPolicy")]
public async Task<IActionResult> AddIpRateLimitPolicy([FromBody] IpRateLimitPolicy ipRateLimitPolicy)
{
var isExist = await _ipRateLimitPolicyService.Queryable()
.Where(it => it.Ip == ipRateLimitPolicy.Ip)
.AnyAsync();
if (isExist) throw new CustomException("该IP已存在");
var res = await _ipRateLimitPolicyService.InsertNav(ipRateLimitPolicy)
.Include(it => it.Rules)
.ExecuteCommandAsync();
return SUCCESS(res);
}
[HttpPut("updateIpRateLimitPolicy")]
public async Task<IActionResult> UpdateIpRateLimitPolicy(
[FromBody] IpRateLimitPolicy ipRateLimitPolicy)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == ipRateLimitPolicy.Id
&& it.Flag == '1')
.AnyAsync();
var res = await _ipRateLimitPolicyService.UpdateNav(ipRateLimitPolicy)
.Include(it => it.Rules)
.ExecuteCommandAsync();
if (!isEnable) return SUCCESS(res);
{
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
var ipRateLimitPolicies = _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync()
.GetAwaiter().GetResult()
.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>();
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies);
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
}
return SUCCESS(res);
}
[HttpPatch("enableIpRateLimitPolicy/{id}")]
public async Task<IActionResult> EnableIpRateLimitPolicy([FromRoute] long id)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == id
&& it.Flag == '1')
.AnyAsync();
if (isEnable)
{
throw new CustomException("已启用,无法再次启用");
}
await _ipRateLimitPolicyService.Updateable(new IpRateLimitPolicy
{
Id = id,
Flag = '1'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
var ipRateLimitPolicy = await _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Id == id)
.SingleAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.Add(ipRateLimitPolicy.Adapt<AspNetCoreRateLimit.IpRateLimitPolicy>());
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
return SUCCESS('1');
}
[HttpPatch("disableIpRateLimitPolicy/{id}")]
public async Task<IActionResult> DisableIpRateLimitPolicy([FromRoute] long id)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == id
&& it.Flag == '1')
.AnyAsync();
if (!isEnable)
{
throw new CustomException("已禁用,无法再次禁用");
}
await _ipRateLimitPolicyService.Updateable(new IpRateLimitPolicy
{
Id = id,
Flag = '0'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
var ipRateLimitPolicies = _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync()
.GetAwaiter().GetResult()
.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies);
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
return SUCCESS('0');
}
[HttpDelete("deleteIpRateLimitPolicy/{id}")]
public async Task DeleteIpRateLimitPolicyAsync([FromRoute] long id)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == id
&& it.Flag == '1')
.AnyAsync();
await _ipRateLimitPolicyService
.DeleteNav(it => it.Id == id)
.Include(it => it.Rules)
.ExecuteCommandAsync();
if (isEnable)
{
var ipRateLimitPolicies = _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync()
.GetAwaiter().GetResult()
.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies);
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
}
}
[HttpGet("getIpRateLimitLogPage")]
public async Task<IActionResult> GetIpRateLimitLogPage([FromQuery] IpRateLimitLog ipRateLimitLog, PagerInfo pager)
{
return SUCCESS(await _ipRateLimitLogService.SelectIpRateLimitLogPageAsync(ipRateLimitLog, pager));
}
[HttpGet("get")]
public async Task<IpRateLimitPolicies> Get()
{
return await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
}
[HttpGet("checkIp/{ip}")]
public async Task<IActionResult> CheckIp(string ip)
{
var res = await _ipRateLimitPolicyService.Queryable()
.Where(it => it.Ip == ip)
.AnyAsync();
return SUCCESS(res);
}
}

View File

@ -2,6 +2,8 @@ using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Diagnostics; using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ZR.Admin.WebApi.Controllers.monitor namespace ZR.Admin.WebApi.Controllers.monitor
@ -48,7 +50,11 @@ namespace ZR.Admin.WebApi.Controllers.monitor
string appRAM = ((double)Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB"; string appRAM = ((double)Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB";
string startTime = Process.GetCurrentProcess().StartTime.ToString("yyyy-MM-dd HH:mm:ss"); string startTime = Process.GetCurrentProcess().StartTime.ToString("yyyy-MM-dd HH:mm:ss");
string sysRunTime = ComputerHelper.GetRunTime(); string sysRunTime = ComputerHelper.GetRunTime();
string serverIP = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP // string serverIP = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
var host = Dns.GetHostEntry(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
var serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
var programStartTime = Process.GetCurrentProcess().StartTime; var programStartTime = Process.GetCurrentProcess().StartTime;
string programRunTime = DateTimeHelper.FormatTime((DateTime.Now - programStartTime).TotalMilliseconds.ToString().Split('.')[0].ParseToLong()); string programRunTime = DateTimeHelper.FormatTime((DateTime.Now - programStartTime).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());

View File

@ -69,6 +69,11 @@
<!--写入黑洞--> <!--写入黑洞-->
<target name="blackhole" xsi:type="Null" /> <target name="blackhole" xsi:type="Null" />
<target name="ipRateLimit" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false"
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-url} ${newline} ${message}">
<highlight-row condition="level == LogLevel.Info" foregroundColor="Blue" />
</target>
</targets> </targets>
<rules> <rules>
@ -82,7 +87,7 @@
<logger name="*.SqlSugar.SqlsugarSetup" final="true" writeTo="consoleSql,sqlfile"/> <logger name="*.SqlSugar.SqlsugarSetup" final="true" writeTo="consoleSql,sqlfile"/>
<logger name="*" minLevel="Trace" writeTo="allfile" /> <logger name="*" minLevel="Trace" writeTo="allfile" />
<logger name="*.GlobalExceptionMiddleware" final="true" writeTo="consoleSql,errorfile"/> <logger name="*.GlobalExceptionMiddleware" final="true" writeTo="consoleSql,errorfile"/>
<logger name="ZR.Admin.WebApi.Middleware.CustomIpRateLimitMiddleware" final="true" writeTo="ipRateLimit" />
<!--Skip non-critical Microsoft logs and so log only own logs--> <!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*,Quartz.Core.QuartzSchedulerThread" maxlevel="Info" final="true" /> <logger name="Microsoft.*,Quartz.Core.QuartzSchedulerThread" maxlevel="Info" final="true" />
</rules> </rules>

View File

@ -3,12 +3,13 @@ using Infrastructure.Converter;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using NLog.Web; using NLog.Web;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Redis; using Microsoft.Extensions.Caching.Redis;
using Microsoft.Extensions.Options;
using ZR.Admin.WebApi.Extensions; using ZR.Admin.WebApi.Extensions;
using ZR.Common.Cache; using ZR.Common.Cache;
using ZR.Infrastructure.WebExtensions; using ZR.Infrastructure.WebExtensions;
using ZR.ServiceCore.Services.IService;
using ZR.ServiceCore.Signalr; using ZR.ServiceCore.Signalr;
using ZR.ServiceCore.SqlSugar; using ZR.ServiceCore.SqlSugar;
@ -31,7 +32,7 @@ builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 跨域配置 // 跨域配置
builder.Services.AddCors(builder.Configuration); builder.Services.AddCors(builder.Configuration);
// 显示logo // 显示logo
builder.Services.AddLogo(); // builder.Services.AddLogo();
//消除Error unprotecting the session cookie警告 //消除Error unprotecting the session cookie警告
builder.Services.AddDataProtection() builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection")); .PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection"));
@ -131,7 +132,8 @@ if (builder.Environment.IsProduction())
//使用swagger //使用swagger
app.UseSwagger(); app.UseSwagger();
//启用客户端IP限制速率 //启用客户端IP限制速率
app.UseIpRateLimiting(); // app.UseIpRateLimiting();
app.UseMiddleware<CustomIpRateLimitMiddleware>();
app.UseRateLimiter(); app.UseRateLimiter();
//设置socket连接 //设置socket连接
app.MapHub<MessageHub>("/msgHub"); app.MapHub<MessageHub>("/msgHub");
@ -141,4 +143,26 @@ app.MapControllerRoute(
pattern: "{controller=Home}/{action=Index}/{id?}"); pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapControllers(); app.MapControllers();
using (var serviceScope = app.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
// get the IpPolicyStore instance
var ipPolicyStore = services.GetRequiredService<IIpPolicyStore>();
// seed IP data from appsettings
ipPolicyStore.SeedAsync().GetAwaiter().GetResult();
var optionsAccessor = services.GetRequiredService<IOptions<IpRateLimitOptions>>();
var ipRateLimitPolicyService = services.GetRequiredService<IIpRateLimitPolicyService>();
var ipRateLimitPolicies = ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync().GetAwaiter().GetResult();
var pol = await ipPolicyStore.GetAsync(optionsAccessor.Value.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies.Adapt<List<IpRateLimitPolicy>>());
await ipPolicyStore.SetAsync(optionsAccessor.Value.IpPolicyPrefix, pol);
}
app.Run(); app.Run();

View File

@ -58,13 +58,37 @@ namespace ZR.Repository
{ {
return Context.Insertable(t); return Context.Insertable(t);
} }
public IInsertable<T> Insertable(List<T> t)
{
return Context.Insertable<T>(t);
}
public IInsertable<T> Insertable(T[] t)
{
return Context.Insertable<T>(t);
}
public InsertNavTaskInit<T, T> InsertNav(T t)
{
return Context.InsertNav(t);
}
public InsertNavTaskInit<T, T> InsertNav(List<T> t)
{
return Context.InsertNav(t);
}
#endregion add #endregion add
#region update #region update
//public IUpdateable<T> Updateable(T entity) public IUpdateable<T> Updateable(T entity)
//{ {
// return Context.Updateable(entity); return Context.Updateable(entity);
//} }
public IUpdateable<T> Updateable(List<T> t)
{
return Context.Updateable<T>(t);
}
public IUpdateable<T> Updateable(T[] t)
{
return Context.Updateable<T>(t);
}
/// <summary> /// <summary>
/// 实体根据主键更新 /// 实体根据主键更新
@ -114,7 +138,25 @@ namespace ZR.Repository
{ {
return Context.Updateable<T>().SetColumns(columns).Where(where).RemoveDataCache().ExecuteCommand(); return Context.Updateable<T>().SetColumns(columns).Where(where).RemoveDataCache().ExecuteCommand();
} }
public UpdateNavTaskInit<T, T> UpdateNav(T t)
{
return Context.UpdateNav(t);
}
public UpdateNavTaskInit<T, T> UpdateNav(List<T> t)
{
return Context.UpdateNav(t);
}
#endregion update #endregion update
public IStorageable<T> Storageable(T t)
{
return Context.Storageable<T>(t);
}
public IStorageable<T> Storageable(List<T> t)
{
return Context.Storageable(t);
}
public DbResult<bool> UseTran(Action action) public DbResult<bool> UseTran(Action action)
{ {
@ -190,6 +232,10 @@ namespace ZR.Repository
{ {
return Context.DbMaintenance.TruncateTable<T>(); return Context.DbMaintenance.TruncateTable<T>();
} }
public DeleteNavTaskInit<T, T> DeleteNav(Expression<Func<T, bool>> whereExpression)
{
return Context.DeleteNav(whereExpression);
}
#endregion delete #endregion delete
#region query #region query

View File

@ -16,9 +16,17 @@ namespace ZR.Repository
int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true); int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true);
IInsertable<T> Insertable(T t); IInsertable<T> Insertable(T t);
IInsertable<T> Insertable(List<T> t);
IInsertable<T> Insertable(T[] t);
InsertNavTaskInit<T, T> InsertNav(T t);
InsertNavTaskInit<T, T> InsertNav(List<T> t);
#endregion add #endregion add
#region update #region update
IUpdateable<T> Updateable(T entity);
IUpdateable<T> Updateable(List<T> t);
IUpdateable<T> Updateable(T[] t);
int Update(T entity, bool ignoreNullColumns = false, object data = null); int Update(T entity, bool ignoreNullColumns = false, object data = null);
/// <summary> /// <summary>
@ -33,7 +41,13 @@ namespace ZR.Repository
int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns); int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns);
UpdateNavTaskInit<T, T> UpdateNav(T t);
UpdateNavTaskInit<T, T> UpdateNav(List<T> t);
#endregion update #endregion update
IStorageable<T> Storageable(T t);
IStorageable<T> Storageable(List<T> t);
DbResult<bool> UseTran(Action action); DbResult<bool> UseTran(Action action);
DbResult<bool> UseTran(SqlSugarClient client, Action action); DbResult<bool> UseTran(SqlSugarClient client, Action action);
@ -46,7 +60,7 @@ namespace ZR.Repository
int Delete(object id, string title = ""); int Delete(object id, string title = "");
int DeleteTable(); int DeleteTable();
bool Truncate(); bool Truncate();
DeleteNavTaskInit<T, T> DeleteNav(Expression<Func<T, bool>> whereExpression);
#endregion delete #endregion delete
#region query #region query

View File

@ -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);
}
}

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; }
}

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';
}

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';
}

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; }
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -19,7 +19,7 @@ namespace ZR.Service.System.IService
/// <param name="rootPath"></param> /// <param name="rootPath"></param>
/// <param name="userName"></param> /// <param name="userName"></param>
/// <returns>文件对象</returns> /// <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); Task<SysFile> SaveFileToAliyun(SysFile file, IFormFile formFile);
/// <summary> /// <summary>

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -37,7 +37,7 @@ namespace ZR.Service.System
/// <param name="formFile">上传的文件流</param> /// <param name="formFile">上传的文件流</param>
/// <param name="userName"></param> /// <param name="userName"></param>
/// <returns></returns> /// <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); string fileExt = Path.GetExtension(formFile.FileName);
fileName = (fileName.IsEmpty() ? HashFileName() : fileName) + fileExt; fileName = (fileName.IsEmpty() ? HashFileName() : fileName) + fileExt;
@ -55,7 +55,7 @@ namespace ZR.Service.System
{ {
await formFile.CopyToAsync(stream); await formFile.CopyToAsync(stream);
} }
string uploadUrl = OptionsSetting.Upload.UploadUrl; // string uploadUrl = OptionsSetting.Upload.UploadUrl;
string accessPath = string.Concat(uploadUrl, "/", filePath.Replace("\\", "/"), "/", fileName); string accessPath = string.Concat(uploadUrl, "/", filePath.Replace("\\", "/"), "/", fileName);
SysFile file = new(formFile.FileName, fileName, fileExt, fileSize + "kb", filePath, userName) SysFile file = new(formFile.FileName, fileName, fileExt, fileSize + "kb", filePath, userName)
{ {

View File

@ -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;
}
}
}