using Asp.Versioning.OpenApi; using MAX.Framework.Security; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi; namespace MAX.Framework.DependencyInjection; public static class TokenAuthServiceCollectionExtensions { // Compatibility entry point; authentication no longer uses JWT. public static IServiceCollection AddStoreHubJwt(this IServiceCollection services, IConfiguration configuration) => services.AddStoreHubTokenAuth(configuration); public static IServiceCollection AddStoreHubTokenAuth(this IServiceCollection services, IConfiguration configuration) { services.AddOptions().Bind(configuration.GetSection("TokenAuth")) .Validate(o => o.IdleDays > 0 && o.IdleDays <= 36500 && o.AbsoluteMaxDays > 0 && o.AbsoluteMaxDays <= 36500 && o.RenewIntervalMinutes > 0 && o.RenewIntervalMinutes < o.IdleDays * 1440 && o.CacheSeconds >= 0 && o.CacheSeconds <= 86400 && o.MaxDevicesPerUser >= 0, "TokenAuth 配置无效:有效期必须为正,续期间隔必须小于闲置有效期,缓存和设备数不能为负。") .ValidateOnStart(); services.AddMemoryCache(); services.AddSingleton(TimeProvider.System); services.AddHttpContextAccessor(); services.AddSingleton(); services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); services.AddScoped(); services.AddAuthentication("Bearer").AddScheme("Bearer", _ => { }); services.Configure(options => { // 在 OpenAPI 文档中声明 Bearer 安全方案 options.Document.AddDocumentTransformer((document, _, _) => { document.Components ??= new OpenApiComponents(); document.Components.SecuritySchemes ??= new Dictionary(); document.Components.SecuritySchemes.TryAdd("Bearer", new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "Opaque", Description = "登录接口返回的 accessToken,直接粘贴令牌内容即可(无需手动加 Bearer 前缀)。", }); return Task.CompletedTask; }); // 仅对标记了 [Authorize] 的接口声明需要携带令牌 options.Document.AddOperationTransformer((operation, context, _) => { IList metadata = context.Description.ActionDescriptor.EndpointMetadata; bool requiresAuth = metadata.OfType().Any() && !metadata.OfType().Any(); if (requiresAuth) { operation.Security = [ new OpenApiSecurityRequirement { [new OpenApiSecuritySchemeReference("Bearer", context.Document, "")] = [], }, ]; } return Task.CompletedTask; }); }); return services; } }