|
|
|
@ -1,186 +1,228 @@
|
|
|
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
|
|
|
using Microsoft.AspNetCore.Cors;
|
|
|
|
|
using Microsoft.OpenApi.Models;
|
|
|
|
|
using Newtonsoft.Json.Converters;
|
|
|
|
|
using NPin.Application;
|
|
|
|
|
using NPin.Framework.AspNetCore;
|
|
|
|
|
using NPin.Framework.AspNetCore.Authentication.OAuth;
|
|
|
|
|
using NPin.Framework.AspNetCore.Authentication.OAuth.Gitee;
|
|
|
|
|
using NPin.Framework.AspNetCore.Authentication.OAuth.QQ;
|
|
|
|
|
using NPin.Framework.AspNetCore.Microsoft.AspNetCore.Builder;
|
|
|
|
|
using NPin.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using NPin.Framework.TenantManagement.Application;
|
|
|
|
|
using NPin.SqlSugarCore;
|
|
|
|
|
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
|
|
|
|
|
using Volo.Abp.AspNetCore.MultiTenancy;
|
|
|
|
|
using Volo.Abp.AspNetCore.Mvc;
|
|
|
|
|
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
|
|
|
|
|
using Volo.Abp.AspNetCore.Serilog;
|
|
|
|
|
using Volo.Abp.Auditing;
|
|
|
|
|
using Volo.Abp.Autofac;
|
|
|
|
|
using Volo.Abp.Caching;
|
|
|
|
|
using Volo.Abp.MultiTenancy;
|
|
|
|
|
using Volo.Abp.Swashbuckle;
|
|
|
|
|
|
|
|
|
|
namespace NPin;
|
|
|
|
|
|
|
|
|
|
[DependsOn(
|
|
|
|
|
typeof(NPinSqlSugarCoreModule),
|
|
|
|
|
typeof(NPinApplicationModule),
|
|
|
|
|
// Abp modules
|
|
|
|
|
typeof(AbpAspNetCoreMultiTenancyModule),
|
|
|
|
|
typeof(AbpAspNetCoreMvcModule),
|
|
|
|
|
typeof(AbpAutofacModule),
|
|
|
|
|
typeof(AbpSwashbuckleModule),
|
|
|
|
|
typeof(AbpAspNetCoreSerilogModule),
|
|
|
|
|
typeof(AbpAuditingModule),
|
|
|
|
|
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
|
|
|
|
|
// Framework modules
|
|
|
|
|
typeof(NPinFrameworkAspNetCoreModule),
|
|
|
|
|
typeof(NPinFrameworkAspNetCoreAuthenticationOAuthModule)
|
|
|
|
|
)]
|
|
|
|
|
public class NPinWebModule : AbpModule
|
|
|
|
|
{
|
|
|
|
|
private const string DefaultCorsPolicyName = "Default";
|
|
|
|
|
|
|
|
|
|
public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
|
|
|
|
|
{
|
|
|
|
|
var configuration = context.Services.GetConfiguration();
|
|
|
|
|
var services = context.Services;
|
|
|
|
|
|
|
|
|
|
// 请求日志
|
|
|
|
|
Configure<AbpAuditingOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
// 默认关闭,开启后有大量的审计日志
|
|
|
|
|
opt.IsEnabled = false;
|
|
|
|
|
// 审计日志过滤器
|
|
|
|
|
opt.AlwaysLogSelectors.Add(_ => Task.FromResult(true));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 动态API
|
|
|
|
|
Configure<AbpAspNetCoreMvcOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.ConventionalControllers.Create(typeof(NPinApplicationModule).Assembly,
|
|
|
|
|
opts => opts.RemoteServiceName = "default");
|
|
|
|
|
// TODO 添加其它模块的动态API
|
|
|
|
|
// TODO Rbac bbs code-gen
|
|
|
|
|
opt.ConventionalControllers.Create(typeof(NPinFrameworkTenantManagementApplicationModule).Assembly,
|
|
|
|
|
opts => opts.RemoteServiceName = "tenant-management");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Api格式配置
|
|
|
|
|
services.AddControllers().AddNewtonsoftJson(opt =>
|
|
|
|
|
{
|
|
|
|
|
// 时间格式
|
|
|
|
|
opt.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
|
|
|
|
// 枚举 <-> string 转换
|
|
|
|
|
opt.SerializerSettings.Converters.Add(new StringEnumConverter());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置分布式缓存不要过期,滑动时间20分钟
|
|
|
|
|
Configure<AbpDistributedCacheOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.GlobalCacheEntryOptions.SlidingExpiration = null;
|
|
|
|
|
// 缓存key前缀
|
|
|
|
|
opt.KeyPrefix = "NPin:";
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 关闭CSRF防伪验证
|
|
|
|
|
Configure<AbpAntiForgeryOptions>(opt => { opt.AutoValidate = false; });
|
|
|
|
|
|
|
|
|
|
// 设置 swagger
|
|
|
|
|
context.Services.AddNPinSwaggerGen<NPinWebModule>(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.SwaggerDoc("default", new OpenApiInfo
|
|
|
|
|
{
|
|
|
|
|
Title = "NPin.Framework",
|
|
|
|
|
Version = "v1",
|
|
|
|
|
Description = "NPin.Framework API文档"
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置跨域
|
|
|
|
|
context.Services.AddCors(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.AddPolicy(DefaultCorsPolicyName, builder =>
|
|
|
|
|
{
|
|
|
|
|
builder.WithOrigins(
|
|
|
|
|
configuration["App:CorsOrigins"]!.Split(";", StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
|
.Select(o => o.RemovePostFix("/"))
|
|
|
|
|
.ToArray()
|
|
|
|
|
)
|
|
|
|
|
.WithAbpExposedHeaders()
|
|
|
|
|
.SetIsOriginAllowedToAllowWildcardSubdomains()
|
|
|
|
|
.AllowAnyHeader()
|
|
|
|
|
.AllowAnyMethod()
|
|
|
|
|
.AllowCredentials();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置 多租户
|
|
|
|
|
Configure<AbpTenantResolveOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
// 只需要 Header 方式的多租户
|
|
|
|
|
// Cookie 方式在 jwt上有坑
|
|
|
|
|
opt.TenantResolvers.Clear();
|
|
|
|
|
opt.TenantResolvers.Add(new HeaderTenantResolveContributor());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置 JWT 鉴权
|
|
|
|
|
// var jwtOptions = configuration.GetSection(nameof(JwtOptions))
|
|
|
|
|
|
|
|
|
|
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
|
|
|
// .AddJwtBearer(opt => { })
|
|
|
|
|
// .AddJwtBearer(opt => { })
|
|
|
|
|
.AddQQ(opt => { configuration.GetSection("OAuth:QQ").Bind(opt); })
|
|
|
|
|
.AddGitee(opt => { configuration.GetSection("OAuth:Gitee").Bind(opt); });
|
|
|
|
|
|
|
|
|
|
// 授权
|
|
|
|
|
context.Services.AddAuthorization();
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
|
|
|
|
|
{
|
|
|
|
|
var app = context.GetApplicationBuilder();
|
|
|
|
|
|
|
|
|
|
app.UseRouting();
|
|
|
|
|
|
|
|
|
|
// Cors
|
|
|
|
|
app.UseCors(DefaultCorsPolicyName);
|
|
|
|
|
|
|
|
|
|
// TODO 无感Token refresh
|
|
|
|
|
|
|
|
|
|
// 多租户
|
|
|
|
|
app.UseMultiTenancy();
|
|
|
|
|
|
|
|
|
|
// Swagger
|
|
|
|
|
app.UseNPinSwagger();
|
|
|
|
|
|
|
|
|
|
// 中间件
|
|
|
|
|
app.UseNPinApiMiddleware();
|
|
|
|
|
|
|
|
|
|
// 静态资源
|
|
|
|
|
app.UseStaticFiles("/api/app/wwwroot");
|
|
|
|
|
app.UseDefaultFiles();
|
|
|
|
|
app.UseDirectoryBrowser("/api/app/wwwroot");
|
|
|
|
|
|
|
|
|
|
// 工作单元
|
|
|
|
|
app.UseUnitOfWork();
|
|
|
|
|
|
|
|
|
|
// 授权
|
|
|
|
|
app.UseAuthorization();
|
|
|
|
|
|
|
|
|
|
// 审计
|
|
|
|
|
app.UseAuditing();
|
|
|
|
|
|
|
|
|
|
// 日志
|
|
|
|
|
app.UseAbpSerilogEnrichers();
|
|
|
|
|
|
|
|
|
|
// 终端节点
|
|
|
|
|
app.UseConfiguredEndpoints();
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Threading.RateLimiting;
|
|
|
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
|
|
|
using Microsoft.AspNetCore.Cors;
|
|
|
|
|
using Microsoft.OpenApi.Models;
|
|
|
|
|
using Newtonsoft.Json.Converters;
|
|
|
|
|
using NPin.Application;
|
|
|
|
|
using NPin.Framework.AspNetCore;
|
|
|
|
|
using NPin.Framework.AspNetCore.Authentication.OAuth;
|
|
|
|
|
using NPin.Framework.AspNetCore.Authentication.OAuth.Gitee;
|
|
|
|
|
using NPin.Framework.AspNetCore.Authentication.OAuth.QQ;
|
|
|
|
|
using NPin.Framework.AspNetCore.Microsoft.AspNetCore.Builder;
|
|
|
|
|
using NPin.Framework.AspNetCore.Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using NPin.Framework.TenantManagement.Application;
|
|
|
|
|
using NPin.Framework.Upms.Application;
|
|
|
|
|
using NPin.SqlSugarCore;
|
|
|
|
|
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
|
|
|
|
|
using Volo.Abp.AspNetCore.MultiTenancy;
|
|
|
|
|
using Volo.Abp.AspNetCore.Mvc;
|
|
|
|
|
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
|
|
|
|
|
using Volo.Abp.AspNetCore.Serilog;
|
|
|
|
|
using Volo.Abp.Auditing;
|
|
|
|
|
using Volo.Abp.Autofac;
|
|
|
|
|
using Volo.Abp.Caching;
|
|
|
|
|
using Volo.Abp.MultiTenancy;
|
|
|
|
|
using Volo.Abp.Swashbuckle;
|
|
|
|
|
|
|
|
|
|
namespace NPin;
|
|
|
|
|
|
|
|
|
|
[DependsOn(
|
|
|
|
|
typeof(NPinSqlSugarCoreModule),
|
|
|
|
|
typeof(NPinApplicationModule),
|
|
|
|
|
// Abp modules
|
|
|
|
|
typeof(AbpAspNetCoreMultiTenancyModule),
|
|
|
|
|
typeof(AbpAspNetCoreMvcModule),
|
|
|
|
|
typeof(AbpAutofacModule),
|
|
|
|
|
typeof(AbpSwashbuckleModule),
|
|
|
|
|
typeof(AbpAspNetCoreSerilogModule),
|
|
|
|
|
typeof(AbpAuditingModule),
|
|
|
|
|
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
|
|
|
|
|
// Framework modules
|
|
|
|
|
typeof(NPinFrameworkAspNetCoreModule),
|
|
|
|
|
typeof(NPinFrameworkAspNetCoreAuthenticationOAuthModule)
|
|
|
|
|
)]
|
|
|
|
|
public class NPinWebModule : AbpModule
|
|
|
|
|
{
|
|
|
|
|
private const string DefaultCorsPolicyName = "Default";
|
|
|
|
|
|
|
|
|
|
public override Task ConfigureServicesAsync(ServiceConfigurationContext context)
|
|
|
|
|
{
|
|
|
|
|
var configuration = context.Services.GetConfiguration();
|
|
|
|
|
var services = context.Services;
|
|
|
|
|
|
|
|
|
|
// 请求日志
|
|
|
|
|
Configure<AbpAuditingOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
// 默认关闭,开启后有大量的审计日志
|
|
|
|
|
opt.IsEnabled = false;
|
|
|
|
|
// 审计日志过滤器
|
|
|
|
|
opt.AlwaysLogSelectors.Add(_ => Task.FromResult(true));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 动态API
|
|
|
|
|
Configure<AbpAspNetCoreMvcOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.ConventionalControllers.Create(typeof(NPinApplicationModule).Assembly,
|
|
|
|
|
opts => opts.RemoteServiceName = "default");
|
|
|
|
|
// TODO 添加其它模块的动态API
|
|
|
|
|
// TODO bbs code-gen
|
|
|
|
|
opt.ConventionalControllers.Create(typeof(NPinFrameworkUpmsApplicationModule).Assembly,
|
|
|
|
|
opts => opts.RemoteServiceName = "upms");
|
|
|
|
|
opt.ConventionalControllers.Create(typeof(NPinFrameworkTenantManagementApplicationModule).Assembly,
|
|
|
|
|
opts => opts.RemoteServiceName = "tenant-management");
|
|
|
|
|
|
|
|
|
|
// 统一API前缀
|
|
|
|
|
opt.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Api格式配置
|
|
|
|
|
services.AddControllers().AddNewtonsoftJson(opt =>
|
|
|
|
|
{
|
|
|
|
|
// 时间格式
|
|
|
|
|
opt.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
|
|
|
|
// 枚举 <-> string 转换
|
|
|
|
|
opt.SerializerSettings.Converters.Add(new StringEnumConverter());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置分布式缓存不要过期,滑动时间20分钟
|
|
|
|
|
Configure<AbpDistributedCacheOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.GlobalCacheEntryOptions.SlidingExpiration = null;
|
|
|
|
|
// 缓存key前缀
|
|
|
|
|
opt.KeyPrefix = "NPin:";
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 关闭CSRF防伪验证
|
|
|
|
|
Configure<AbpAntiForgeryOptions>(opt => { opt.AutoValidate = false; });
|
|
|
|
|
|
|
|
|
|
// 设置 swagger
|
|
|
|
|
context.Services.AddNPinSwaggerGen<NPinWebModule>(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.SwaggerDoc("default", new OpenApiInfo
|
|
|
|
|
{
|
|
|
|
|
Title = "NPin.Framework",
|
|
|
|
|
Version = "v1",
|
|
|
|
|
Description = "NPin.Framework API文档"
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置跨域
|
|
|
|
|
context.Services.AddCors(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.AddPolicy(DefaultCorsPolicyName, builder =>
|
|
|
|
|
{
|
|
|
|
|
builder.WithOrigins(
|
|
|
|
|
configuration["App:CorsOrigins"]!.Split(";", StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
|
.Select(o => o.RemovePostFix("/"))
|
|
|
|
|
.ToArray()
|
|
|
|
|
)
|
|
|
|
|
.WithAbpExposedHeaders()
|
|
|
|
|
.SetIsOriginAllowedToAllowWildcardSubdomains()
|
|
|
|
|
.AllowAnyHeader()
|
|
|
|
|
.AllowAnyMethod()
|
|
|
|
|
.AllowCredentials();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置 多租户
|
|
|
|
|
Configure<AbpTenantResolveOptions>(opt =>
|
|
|
|
|
{
|
|
|
|
|
// 只需要 Header 方式的多租户
|
|
|
|
|
// Cookie 方式在 jwt上有坑
|
|
|
|
|
opt.TenantResolvers.Clear();
|
|
|
|
|
opt.TenantResolvers.Add(new HeaderTenantResolveContributor());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//速率限制
|
|
|
|
|
//每60秒限制100个请求,滑块添加,分6段
|
|
|
|
|
context.Services.AddRateLimiter(opt =>
|
|
|
|
|
{
|
|
|
|
|
opt.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
|
|
|
|
|
opt.OnRejected = (ctx, _) =>
|
|
|
|
|
{
|
|
|
|
|
if (ctx.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
|
|
|
|
|
{
|
|
|
|
|
ctx.HttpContext.Response.Headers.RetryAfter =
|
|
|
|
|
((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
|
|
|
|
|
ctx.HttpContext.Response.WriteAsync("太多请求了,请稍后再试。");
|
|
|
|
|
return new ValueTask();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 全局使用,链式表达式
|
|
|
|
|
opt.GlobalLimiter = PartitionedRateLimiter.CreateChained(PartitionedRateLimiter.Create<HttpContext, string>(
|
|
|
|
|
httpContext =>
|
|
|
|
|
{
|
|
|
|
|
var userAgent = httpContext.Request.Headers.UserAgent.ToString();
|
|
|
|
|
return RateLimitPartition.GetSlidingWindowLimiter(userAgent, _ =>
|
|
|
|
|
new SlidingWindowRateLimiterOptions
|
|
|
|
|
{
|
|
|
|
|
PermitLimit = 1000,
|
|
|
|
|
Window = TimeSpan.FromSeconds(60),
|
|
|
|
|
SegmentsPerWindow = 6,
|
|
|
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
|
|
|
|
|
});
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 配置 JWT 鉴权
|
|
|
|
|
// var jwtOptions = configuration.GetSection(nameof(JwtOptions))
|
|
|
|
|
|
|
|
|
|
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
|
|
|
// .AddJwtBearer(opt => { })
|
|
|
|
|
// .AddJwtBearer(opt => { })
|
|
|
|
|
.AddQQ(opt => { configuration.GetSection("OAuth:QQ").Bind(opt); })
|
|
|
|
|
.AddGitee(opt => { configuration.GetSection("OAuth:Gitee").Bind(opt); });
|
|
|
|
|
|
|
|
|
|
// 授权
|
|
|
|
|
context.Services.AddAuthorization();
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
|
|
|
|
|
{
|
|
|
|
|
var app = context.GetApplicationBuilder();
|
|
|
|
|
|
|
|
|
|
app.UseRouting();
|
|
|
|
|
|
|
|
|
|
// Cors
|
|
|
|
|
app.UseCors(DefaultCorsPolicyName);
|
|
|
|
|
|
|
|
|
|
// TODO 无感Token refresh
|
|
|
|
|
|
|
|
|
|
// 多租户
|
|
|
|
|
app.UseMultiTenancy();
|
|
|
|
|
|
|
|
|
|
// Swagger
|
|
|
|
|
app.UseNPinSwagger();
|
|
|
|
|
|
|
|
|
|
// 中间件
|
|
|
|
|
app.UseNPinApiMiddleware();
|
|
|
|
|
|
|
|
|
|
// 静态资源
|
|
|
|
|
app.UseStaticFiles("/api/app/wwwroot");
|
|
|
|
|
app.UseDefaultFiles();
|
|
|
|
|
app.UseDirectoryBrowser("/api/app/wwwroot");
|
|
|
|
|
|
|
|
|
|
// 工作单元
|
|
|
|
|
app.UseUnitOfWork();
|
|
|
|
|
|
|
|
|
|
// 授权
|
|
|
|
|
app.UseAuthorization();
|
|
|
|
|
|
|
|
|
|
// 审计
|
|
|
|
|
app.UseAuditing();
|
|
|
|
|
|
|
|
|
|
// 日志
|
|
|
|
|
app.UseAbpSerilogEnrichers();
|
|
|
|
|
|
|
|
|
|
// 终端节点
|
|
|
|
|
app.UseConfiguredEndpoints();
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
}
|