// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure <CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.Secure = CookieSecurePolicy.SameAsRequest; options.OnAppendCookie = cookieContext => AuthenticationHelper.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => AuthenticationHelper.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); }); services.AddHealthChecks(); services.AddJsonLocalization(options => { options.ResourcesPath = Configuration.GetAppSetting("ResourcesPath"); options.ResourcesPathType = ResourcesPathType.CultureBased; }); services.AddResponseCaching(); services.AddResponseCompression(); services.AddControllersWithViews(options => { options.CacheProfiles.Add("default", new CacheProfile() { Duration = 300, VaryByQueryKeys = new[] { "*" } }); options.CacheProfiles.Add("private", new CacheProfile() { Duration = 300, Location = ResponseCacheLocation.Client, VaryByQueryKeys = new[] { "*" } }); options.CacheProfiles.Add("noCache", new CacheProfile() { Duration = null, NoStore = true }); }) .AddNewtonsoftJson(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; // 设置时区为 UTC options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) .AddViewLocalization() .AddDataAnnotationsLocalization() ; var supportedCultureNames = Configuration.GetSection("Localization:SupportedCultures")?.Get <string[]>(); if (supportedCultureNames == null || supportedCultureNames.Length == 0) { supportedCultureNames = new[] { "zh", "en" }; } var supportedCultures = supportedCultureNames.Select(name => new CultureInfo(name)).ToArray(); services.Configure <RequestLocalizationOptions>(options => { options.DefaultRequestCulture = new RequestCulture(supportedCultures[0].Name); // Formatting numbers, dates, etc. options.SupportedCultures = supportedCultures; // UI strings that we have localized. options.SupportedUICultures = supportedCultures; }); //Cookie Authentication services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultForbidScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { //options.LoginPath = "/Admin/Account/Login"; options.AccessDeniedPath = "/Account/AccessDenied"; options.LogoutPath = "/Account/LogOut"; // Cookie settings options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.None; }) .AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme, options => { options.Authority = Configuration["Authorization:Authority"]; options.RequireHttpsMetadata = false; options.NameClaimType = "name"; options.RoleClaimType = "role"; }) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { var authorizationConfiguration = Configuration.GetSection("Authorization"); authorizationConfiguration.Bind(options); options.ResponseType = OpenIdConnectResponseType.CodeIdToken; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.ClaimActions.MapJsonKey("role", "role"); options.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = "name", RoleClaimType = "role", }; options.Events.OnMessageReceived = context => { context.Properties.IsPersistent = true; return(Task.CompletedTask); }; options.Events.OnRedirectToIdentityProvider = rc => { rc.ProtocolMessage.RedirectUri = authorizationConfiguration["RedirectUri"]; return(Task.CompletedTask); }; }) ; services.AddAuthorization(options => { options.AddPolicy("ReservationManager", builder => builder .AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .RequireRole("ReservationManager", "ReservationAdmin") ); options.AddPolicy("ReservationAdmin", builder => builder .AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .RequireRole("ReservationAdmin") ); options.AddPolicy("ReservationApi", builder => builder .AddAuthenticationSchemes(IdentityServerAuthenticationDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .RequireScope("ReservationApi") ); }); // addDbContext services.AddDbContextPool <ReservationDbContext>(option => { var dbType = Configuration.GetAppSetting("DbType"); if ("InMemory".EqualsIgnoreCase(dbType)) { option.UseInMemoryDatabase("Reservation"); } else if ("MySql".EqualsIgnoreCase(dbType)) { option.UseMySql(Configuration.GetConnectionString("Reservation")); } else { option.UseSqlServer(Configuration.GetConnectionString("Reservation")); } }, 100); services.AddGoogleRecaptchaHelper(Configuration.GetSection("GoogleRecaptcha"), client => { client.Timeout = TimeSpan.FromSeconds(3); }); services.AddTencentCaptchaHelper(options => { options.AppId = Configuration["Tencent:Captcha:AppId"]; options.AppSecret = Configuration["Tencent:Captcha:AppSecret"]; }, client => { client.Timeout = TimeSpan.FromSeconds(3); }); services.AddHttpClient <ChatBotHelper>(client => { client.Timeout = TimeSpan.FromSeconds(5); }); services.TryAddSingleton <ChatBotHelper>(); services.AddHttpClient <WechatAPI.Helper.WeChatHelper>(); services.TryAddSingleton <WechatAPI.Helper.WeChatHelper>(); // registerApplicationSettingService if (HostEnvironment.IsDevelopment()) { services.TryAddSingleton <IApplicationSettingService, ApplicationSettingInMemoryService>(); services.TryAddSingleton <ICacheClient, InMemoryCacheClient>(); } else { services.TryAddSingleton <IApplicationSettingService, ApplicationSettingInRedisService>(); } // register access control service services.AddAccessControlHelper() .AddResourceAccessStrategy <AdminPermissionRequireStrategy>() .AddControlAccessStrategy <AdminOnlyControlAccessStrategy>() ; // DataProtection persist in redis var dataProtectionBuilder = services.AddDataProtection() .SetApplicationName(ApplicationHelper.ApplicationName); if (!HostEnvironment.IsDevelopment()) { services.AddRedisConfig(options => { options.DefaultDatabase = 0; options.RedisServers = new[] { new RedisServerConfiguration(Configuration.GetConnectionString("Redis") ?? "127.0.0.1"), }; options.CachePrefix = "OpenReservation"; }); dataProtectionBuilder.PersistKeysToStackExchangeRedis( () => DependencyResolver.Current .ResolveService <IDatabase>(), "DataProtection-Keys"); } // events services.AddEvents() .AddEventHandler <NoticeViewEvent, NoticeViewEventHandler>() .AddEventHandler <OperationLogEvent, OperationLogEventHandler>() ; services.Configure <CustomExceptionHandlerOptions>(options => { options.OnRequestAborted = (context, logger) => Task.CompletedTask; options.OnException = (context, logger, exception) => { var ex = exception; if (exception is AggregateException aggregateException) { ex = aggregateException.Unwrap(); } if (context.RequestAborted.IsCancellationRequested && ( ex is TaskCanceledException || ex is OperationCanceledException) ) { return(Task.CompletedTask); } logger.LogError(exception, exception.Message); return(Task.CompletedTask); }; }); // gitee storage services.AddGiteeStorageProvider(Configuration.GetSection("Storage:Gitee")); services.AddSwaggerGen(options => { options.SwaggerDoc(ApplicationHelper.ApplicationName, new OpenApiInfo { Title = "活动室预约系统 API", Version = "1.0" }); options.IncludeXmlComments(System.IO.Path.Combine(AppContext.BaseDirectory, $"{typeof(Notice).Assembly.GetName().Name}.xml")); options.IncludeXmlComments(System.IO.Path.Combine(AppContext.BaseDirectory, $"{typeof(API.NoticeController).Assembly.GetName().Name}.xml"), true); // Add security definitions options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Description = "Please enter into field the word 'Bearer' followed by a space and the JWT value", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference() { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, Array.Empty <string>() } }); }); services.AddHttpContextUserIdProvider(options => { options.UserIdFactory = context => { var user = context?.User; if (null != user && user.Identity.IsAuthenticated) { return($"{user.GetUserId()}--{user.Identity.Name}"); } var userIp = context?.GetUserIP(); if (null != userIp) { return(userIp); } return($"{Environment.MachineName}__{Environment.UserName}"); }; }); // RegisterAssemblyModules services.RegisterAssemblyModules(); }