You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 18:14:53