using AspNetCoreRateLimit; using Autofac; using IdentityServer4.AccessTokenValidation; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyModel; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Reflection; using System.Text; using Mapster; using Yitter.IdGenerator; //using FluentValidation; //using FluentValidation.AspNetCore; using ZhonTai.Admin.Core.Auth; using ZhonTai.Common.Auth; using ZhonTai.Tools.Cache; using ZhonTai.Common.Configs; using ZhonTai.Common.Consts; using ZhonTai.Common.Helpers; using ZhonTai.Admin.Core.Db; using ZhonTai.Admin.Core.Enums; using ZhonTai.Admin.Core.Extensions; using ZhonTai.Admin.Core.Filters; using ZhonTai.Admin.Core.Logs; using ZhonTai.Admin.Core.RegisterModules; using MapsterMapper; using StackExchange.Profiling; using System.IO; using ZhonTai.Tools.DynamicApi; using Microsoft.OpenApi.Any; using Microsoft.AspNetCore.Mvc.Controllers; using ZhonTai.Admin.Core.Attributes; using Microsoft.AspNetCore.Cors; using ZhonTai.ApiUI; namespace ZhonTai.Admin.Core { public abstract class BaseStartup { protected static string basePath => AppContext.BaseDirectory; protected readonly IConfiguration _configuration; protected readonly IHostEnvironment _env; protected readonly ConfigHelper _configHelper; protected readonly AppConfig _appConfig; protected const string DefaultCorsPolicyName = "AllowPolicy"; public BaseStartup(IConfiguration configuration, IWebHostEnvironment env) { _configuration = configuration; _env = env; _configHelper = new ConfigHelper(); _appConfig = _configHelper.Get("appconfig", env.EnvironmentName) ?? new AppConfig(); } /// /// 配置服务 /// /// public virtual void ConfigureServices(IServiceCollection services) { //雪花漂移算法 YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1) { WorkerIdBitLength = 6 }); //权限处理 services.AddScoped(); // ClaimType不被更改 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //用户信息 services.AddSingleton(); if (_appConfig.IdentityServer.Enable) { //is4 services.TryAddSingleton(); } else { //jwt services.TryAddSingleton(); } //添加数据库 services.AddDbAsync(_env).Wait(); //添加IdleBus单例 var dbConfig = new ConfigHelper().Get("dbconfig", _env.EnvironmentName); var timeSpan = dbConfig.IdleTime > 0 ? TimeSpan.FromMinutes(dbConfig.IdleTime) : TimeSpan.MaxValue; IdleBus ib = new IdleBus(timeSpan); services.AddSingleton(ib); //数据库配置 services.AddSingleton(dbConfig); //应用配置 services.AddSingleton(_appConfig); //上传配置 var uploadConfig = _configHelper.Load("uploadconfig", _env.EnvironmentName, true); services.Configure(uploadConfig); #region Mapster 映射配置 Assembly[] assemblies = DependencyContext.Default.RuntimeLibraries .Where(a => a.Name.StartsWith("ZhonTai")) .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray(); services.AddScoped(sp => new Mapper()); TypeAdapterConfig.GlobalSettings.Scan(assemblies); #endregion Mapster 映射配置 #region Cors 跨域 services.AddCors(options => { options.AddPolicy(DefaultCorsPolicyName, policy => { var hasOrigins = _appConfig.CorUrls?.Length > 0; if (hasOrigins) { policy.WithOrigins(_appConfig.CorUrls); } else { policy.AllowAnyOrigin(); } policy .AllowAnyHeader() .AllowAnyMethod(); if (hasOrigins) { policy.AllowCredentials(); } }); //允许任何源访问Api策略,使用时在控制器或者接口上增加特性[EnableCors(AdminConsts.AllowAnyPolicyName)] options.AddPolicy(AdminConsts.AllowAnyPolicyName, policy => { policy .AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); /* //浏览器会发起2次请求,使用OPTIONS发起预检请求,第二次才是api异步请求 options.AddPolicy("All", policy => { policy .AllowAnyOrigin() .SetPreflightMaxAge(new TimeSpan(0, 10, 0)) .AllowAnyHeader() .AllowAnyMethod(); }); */ }); #endregion Cors 跨域 #region 身份认证授权 var jwtConfig = _configHelper.Get("jwtconfig", _env.EnvironmentName); services.TryAddSingleton(jwtConfig); if (_appConfig.IdentityServer.Enable) { //is4 services.AddAuthentication(options => { options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401 options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403 }) .AddJwtBearer(options => { options.Authority = _appConfig.IdentityServer.Url; options.RequireHttpsMetadata = false; options.Audience = "admin.server.api"; }) .AddScheme(nameof(ResponseAuthenticationHandler), o => { }); } else { //jwt services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401 options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403 }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtConfig.Issuer, ValidAudience = jwtConfig.Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey)), ClockSkew = TimeSpan.Zero }; }) .AddScheme(nameof(ResponseAuthenticationHandler), o => { }); } #endregion 身份认证授权 #region Swagger Api文档 if (_env.IsDevelopment() || _appConfig.Swagger.Enable) { services.AddSwaggerGen(options => { typeof(ApiVersion).GetEnumNames().ToList().ForEach(version => { options.SwaggerDoc(version, new OpenApiInfo { Version = version, Title = "ZhonTai.Admin.Host" }); //c.OrderActionsBy(o => o.RelativePath); }); options.SchemaFilter(); options.CustomOperationIds(apiDesc => { var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor; return controllerAction.ControllerName + "-" + controllerAction.ActionName; }); options.ResolveConflictingActions(apiDescription => apiDescription.First()); options.CustomSchemaIds(x => x.FullName); options.DocInclusionPredicate((docName, description) => true); string[] xmlFiles = Directory.GetFiles(basePath, "*.xml"); if (xmlFiles.Length > 0) { foreach (var xmlFile in xmlFiles) { options.IncludeXmlComments(xmlFile, true); } } var server = new OpenApiServer() { Url = _appConfig.Swagger.Url, Description = "" }; server.Extensions.Add("extensions", new OpenApiObject { ["copyright"] = new OpenApiString(_appConfig.ApiUI.Footer.Content) }); options.AddServer(server); #region 添加设置Token的按钮 if (_appConfig.IdentityServer.Enable) { //添加Jwt验证设置 options.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "oauth2", Type = ReferenceType.SecurityScheme } }, new List() } }); //统一认证 options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, Description = "oauth2登录授权", Flows = new OpenApiOAuthFlows { Implicit = new OpenApiOAuthFlow { AuthorizationUrl = new Uri($"{_appConfig.IdentityServer.Url}/connect/authorize"), Scopes = new Dictionary { { "admin.server.api", "admin后端api" } } } } }); } else { //添加Jwt验证设置 options.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, new List() } }); options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "Value: Bearer {token}", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey }); } #endregion 添加设置Token的按钮 }); } #endregion Swagger Api文档 #region 操作日志 if (_appConfig.Log.Operation) { services.AddScoped(); } #endregion 操作日志 #region 控制器 services.AddControllers(options => { options.Filters.Add(); options.Filters.Add(); options.Filters.Add(); if (_appConfig.Log.Operation) { options.Filters.Add(); } //禁止去除ActionAsync后缀 //options.SuppressAsyncSuffixInActionNames = false; }) //.AddFluentValidation(config => //{ // var assembly = Assembly.LoadFrom(Path.Combine(basePath, "ZhonTai.Admin.Host.dll")); // config.RegisterValidatorsFromAssembly(assembly); //}) .AddNewtonsoftJson(options => { //忽略循环引用 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //使用驼峰 首字母小写 options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //设置时间格式 options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }) .AddControllersAsServices(); #endregion 控制器 services.AddHttpClient(); #region 缓存 var cacheConfig = _configHelper.Get("cacheconfig", _env.EnvironmentName); if (cacheConfig.Type == CacheType.Redis) { var csredis = new CSRedis.CSRedisClient(cacheConfig.Redis.ConnectionString); RedisHelper.Initialization(csredis); services.AddSingleton(); } else { services.AddMemoryCache(); services.AddSingleton(); } #endregion 缓存 #region IP限流 if (_appConfig.RateLimit) { services.AddIpRateLimit(_configuration, cacheConfig); } #endregion IP限流 //阻止NLog接收状态消息 services.Configure(opts => opts.SuppressStatusMessages = true); //性能分析 if (_appConfig.MiniProfiler) { services.AddMiniProfiler(); } //动态api services.AddDynamicApi(options => { Assembly[] assemblies = DependencyContext.Default.RuntimeLibraries .Where(a => a.Name.EndsWith("Service")) .Select(o => Assembly.Load(new AssemblyName(o.Name))).ToArray(); options.AddAssemblyOptions(assemblies); }); } /// /// 配置容器 /// /// /// public virtual void ConfigureContainer(ContainerBuilder builder) { #region AutoFac IOC容器 try { // 控制器注入 builder.RegisterModule(new ControllerModule()); // 单例注入 builder.RegisterModule(new SingleInstanceModule()); // 仓储注入 builder.RegisterModule(new RepositoryModule()); // 服务注入 builder.RegisterModule(new ServiceModule(_appConfig)); } catch (Exception ex) { throw new Exception(ex.Message + "\n" + ex.InnerException); } #endregion AutoFac IOC容器 } /// /// 配置中间件 /// /// public virtual void Configure(IApplicationBuilder app) { #region app配置 //IP限流 if (_appConfig.RateLimit) { app.UseIpRateLimiting(); } //性能分析 if (_appConfig.MiniProfiler) { app.UseMiniProfiler(); } //异常 app.UseExceptionHandler("/Error"); //静态文件 app.UseDefaultFiles(); app.UseStaticFiles(); app.UseUploadConfig(); //路由 app.UseRouting(); //跨域 app.UseCors(DefaultCorsPolicyName); //认证 app.UseAuthentication(); //授权 app.UseAuthorization(); //配置端点 app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); #endregion app配置 #region Swagger Api文档 if (_env.IsDevelopment() || _appConfig.Swagger.Enable) { app.UseSwagger(); app.UseSwaggerUI(c => { typeof(ApiVersion).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => { c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"ZhonTai.Admin.Host {version}"); }); c.RoutePrefix = "";//直接根目录访问,如果是IIS发布可以注释该语句,并打开launchSettings.launchUrl c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);//折叠Api //c.DefaultModelsExpandDepth(-1);//不显示Models if (_appConfig.MiniProfiler) { c.InjectJavascript("/swagger/mini-profiler.js?v=4.2.22"); } }); } #endregion Swagger Api文档 if (_env.IsDevelopment() || _appConfig.ApiUI.Enable) { app.UseApiUI(options => { options.RoutePrefix = "swagger"; typeof(ApiVersion).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => { options.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"ZhonTai.Host {version}"); }); }); } //数据库日志 //var log = LogManager.GetLogger("db"); //var ei = new LogEventInfo(LogLevel.Error, "", "错误信息"); //ei.Properties["id"] = YitIdHelper.NextId(); //log.Log(ei); } } }