using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using QYZH.InteractiveMagazine.Infrastructure.Middleware; using QYZH.InteractiveMagazine.Models.Settings; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; namespace QYZH.InteractiveMagazine.Infrastructure.Extensions; /// /// 统一服务注册扩展 /// public static class DependencyInjectionExtensions { /// /// 注册基础设施服务 /// /// 服务集合 /// 配置 /// 运行环境 public static void AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment? environment = null) { AddJwtAuthentication(services, configuration, environment); services.AddTransient(); services.AddTransient(); } /// /// 配置JWT认证 /// /// 服务集合 /// 配置 /// 运行环境 private static void AddJwtAuthentication(IServiceCollection services, IConfiguration configuration, IWebHostEnvironment? environment = null) { // 开发环境下跳过 JWT 验证 if (environment?.IsDevelopment() == true) { services.AddAuthentication("NoAuth") .AddScheme("NoAuth", options => { }); return; } var jwtSettings = configuration.GetSection("JwtSettings").Get()!; services.AddSingleton(jwtSettings); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = jwtSettings.Issuer, ValidateAudience = true, ValidAudience = jwtSettings.Audience, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey!)), ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; options.Events = new JwtBearerEvents { OnTokenValidated = async context => { var userId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { context.Fail("无效的 Token"); return; } // 检查管理后台 Token var adminToken = await RedisHelper.GetAsync($"InteractiveMagazine:AdminAuth:Token:{userId}"); if (!string.IsNullOrEmpty(adminToken)) { return; } // 检查微信端 Token var wechatToken = await RedisHelper.GetAsync($"InteractiveMagazine:WeChatAuth:Token:{userId}"); if (!string.IsNullOrEmpty(wechatToken)) { return; } // Redis 中不存在任何 Token,认证失败 context.Fail("Token 已失效,请重新登录"); } }; }); } } /// /// 开发环境免认证处理器 /// public class NoAuthHandler : AuthenticationHandler { public NoAuthHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task HandleAuthenticateAsync() { // 开发环境下始终认证成功 var claims = new[] { new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name, "DevUser"), new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.NameIdentifier, "0") }; var identity = new System.Security.Claims.ClaimsIdentity(claims, Scheme.Name); var principal = new System.Security.Claims.ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return Task.FromResult(AuthenticateResult.Success(ticket)); } }