ASP.NET Web API的Swagger Bearer认证失效,Postman可正常使用
ASP.NET Web API Firebase JWT认证:Swagger UI调用401但Postman正常的问题
我在为ASP.NET Web API配置Firebase Google作为身份提供者的JWT认证时遇到了问题:在Swagger UI中可以生成JWT Token并通过“Authorize”按钮添加,但调用需要认证的接口时返回401错误;而用Postman发送完全相同的请求却能得到200响应。此外,Swagger UI完成认证后,锁图标显示异常(未正常切换为已解锁状态)。
初始配置片段
Swagger生成配置
builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "WorkoutBuddy", Version = "v1" }); options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Name = "Authorization", Description = "Provide JWT token", Type = SecuritySchemeType.Http, BearerFormat = "JWT", Scheme = "bearer" }); options.AddSecurityRequirement(new OpenApiSecurityRequirement{ { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type=ReferenceType.SecurityScheme, Id="Bearer" } }, Array.Empty<string>() } }); });
JWT配置
builder.Services .AddAuthentication() .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtOptions => { jwtOptions.Authority = builder.Configuration["Auth:ValidIssuer"]; jwtOptions.Audience = builder.Configuration["Auth:Audience"]; jwtOptions.TokenValidationParameters.ValidIssuer = builder.Configuration["Auth:ValidIssuer"]; });
Swagger UI中间件配置
app.UseExceptionHandler("/error") .UseSwagger() .UseSwaggerUI(c => { c.SwaggerEndpoint($"/swagger/v1/swagger.json", "WorkoutBuddy Api"); }); app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
更新:完整Program.cs代码
global using WorkoutBuddy.Data; using Microsoft.EntityFrameworkCore; using Azure.Identity; using Microsoft.AspNetCore.Authentication.JwtBearer; using FirebaseAdmin; using Google.Apis.Auth.OAuth2; using WorkoutBuddy.Services; using WorkoutBuddy.Features; using Microsoft.OpenApi.Models; using Microsoft.IdentityModel.Tokens; using Swashbuckle.AspNetCore.Filters; var builder = WebApplication.CreateBuilder(args); var env = builder.Environment; builder.Configuration .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables() .AddUserSecrets<Program>(); // Setup EF Core to SQL db connection builder.Services.AddDbContext<DataContext>(options => { var sqlDbConnectionString = builder.Configuration.GetConnectionString("SQL") ?? throw new ArgumentNullException("Missing SQL connectionstring"); options.UseSqlServer(sqlDbConnectionString, sqlServerOptions => { sqlServerOptions.CommandTimeout(30); }); }); // setup swagger options builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "WorkoutBuddy", Version = "v1" }); options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Name = "Authorization", Description = "Provide JWT token", Type = SecuritySchemeType.Http, BearerFormat = "JWT", Scheme = "bearer" }); // options.AddSecurityRequirement(new OpenApiSecurityRequirement{ // { // new OpenApiSecurityScheme // { // Reference = new OpenApiReference // { // Type=ReferenceType.SecurityScheme, // Id="Bearer" // } // }, // Array.Empty<string>() // } // }); options.OperationFilter<SecurityRequirementsOperationFilter>(); }); // setup firebase authentication var firebaseConfig = builder.Configuration.GetValue<string>("Auth:FirebaseConfig"); FirebaseApp.Create(new AppOptions() { Credential = GoogleCredential.FromJson(firebaseConfig) }); builder.Services .AddAuthentication() .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtOptions => { jwtOptions.Authority = builder.Configuration["Auth:ValidIssuer"]; jwtOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration.GetValue<string>("Auth:ValidIssuer"), ValidAudience = builder.Configuration.GetValue<string>("Auth:Audience") }; jwtOptions.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { Console.WriteLine("Unauthorized request: \n" + context.Exception); return Task.CompletedTask; } }; }); // setup services builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpClient<GoogleJwtProvider, GoogleJwtProvider>(httpClient => { var baseAddress = builder.Configuration["Auth:TokenUri"] ?? throw new ArgumentNullException("Missing: Auth:TokenUri"); var apiKey = builder.Configuration["Auth:FirebaseApiKey"] ?? throw new ArgumentException("Missing firebase api key"); httpClient.BaseAddress = new Uri($"{baseAddress}?key={apiKey}"); }); builder.Services.AddScoped<UserService, UserService>(); builder.Services.AddScoped<ProfileService, ProfileService>(); builder.Services.AddScoped<WorkoutDetailService, WorkoutDetailService>(); builder.Services.AddScoped<ExerciseDetailService, ExerciseDetailService>(); builder.Services.AddScoped<WorkoutService, WorkoutService>(); var app = builder.Build(); switch (args.FirstOrDefault()) { case "initAndSeedDb": await app.InitAndSeedDb(); return; case null: RunApp(app); return; case var arg: throw new ArgumentException($"Unknown command-line argument: {arg}"); } static void RunApp(WebApplication app) { app.UseSwagger() .UseSwaggerUI(c => { c.SwaggerEndpoint($"/swagger/v1/swagger.json", "WorkoutBuddy Api"); }); app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); }
测试用控制器代码
[Authorize] [ApiController] [Route("api/workout")] public class WorkoutController : CustomControllerBase { private readonly ILogger<WorkoutController> _logger; private readonly WorkoutService _workoutService; public WorkoutController( ILogger<WorkoutController> logger, WorkoutService workoutService) { _logger = logger; _workoutService = workoutService; } [HttpGet()] public async Task<ActionResult<IEnumerable<WorkoutResponse>>> GetAllWorkoutsByProfile() { var workoutsResult = await _workoutService.GetWorkoutsForProfile(); return GetDataOrError( result: workoutsResult, resolveResponse: (w) => w.Select(e => new WorkoutResponse(e)) ); } [HttpGet("{id}")] public async Task<ActionResult<WorkoutResponse>> GetWorkoutById([FromRoute][Required] Guid workoutId) { var workoutResult = await _workoutService.GetWorkoutById(workoutId); return GetDataOrError( result: workoutResult, resolveResponse: (w) => new WorkoutResponse(w) ); } [HttpDelete("{id}")] public async Task<ActionResult<WorkoutResponse>> DeleteWorkout([FromRoute][Required] Guid workoutId) { var workoutResult = await _workoutService.DeleteWorkout(workoutId); return GetDataOrError( result: workoutResult, resolveResponse: (w) => new WorkoutResponse(w) ); } }
问题排查与解决方案
- Swagger认证头格式问题:检查Swagger的Authorize弹窗,确保只输入纯Token字符串,不要手动添加
Bearer前缀(Swagger会自动根据配置添加),避免出现Bearer Bearer xxx的错误头格式。 - 安全需求配置冲突:注释掉
options.OperationFilter<SecurityRequirementsOperationFilter>();,恢复手动添加的AddSecurityRequirement配置,避免重复配置导致Swagger锁图标异常。 - Firebase配置校验:确认
Auth:ValidIssuer为https://securetoken.google.com/<你的Firebase项目ID>,Auth:Audience为你的Firebase项目ID,确保JWT验证参数与Firebase要求匹配。 - 查看认证失败日志:利用
OnAuthenticationFailed事件输出的异常信息,定位具体失败原因(如Issuer不匹配、签名验证失败等),针对性调整配置。
内容的提问来源于stack exchange,提问作者Frederik
相关产品推荐
相关产品推荐

