近期,多位.NET开发者反馈,在ASP.NET Core Web API项目中集成Swagger(即Swashbuckle或NSwag)后,调用API接口时始终返回401 Unauthorized错误,即便在Swagger UI中已成功配置认证令牌(Bearer Token)。这一问题严重影响了开发调试效率,尤其在开启JWT认证或Azure AD保护的接口中频繁出现。本文梳理了导致该现象的几个核心原因,并提供经过验证的修复方案。

问题现象:Token明明已传,为何仍报401?

开发者通常在Program.cs中配置了AddAuthenticationAddSwaggerGen,并在Swagger UI的“Authorize”对话框中填入了有效的Bearer Token。点击“Try it out”发送请求后,控制台或响应体却返回HTTP 401,错误信息多为“Missing Authorization Header”或“Invalid token”。更诡异的是,使用Postman或curl发送同一token却能正常通过。问题根源往往不在Authentication中间件本身,而在于Swagger与认证机制的集成细节。

原因一:Swagger未正确配置SecurityDefinition与SecurityRequirement

这是最常见的原因。仅仅在AddSwaggerGen中定义AddSecurityDefinition是不够的,必须同时指定AddSecurityRequirement,否则Swagger生成的openapi.json中不会携带安全方案声明,UI虽然能手动输入token,但实际请求头中却不会被附加。

正确配置示例(Swashbuckle 6.x):

builder.Services.AddSwaggerGen(c =>
{
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty<string>()
        }
    });
});

注意: 使用SecuritySchemeType.HttpScheme = "bearer"也是常见写法,但需确保与认证方案的Scheme名称完全一致(大小写敏感)。

原因二:认证中间件注册顺序错误

ASP.NET Core中间件管道执行顺序至关重要。常见的错误是将UseAuthentication放在UseSwaggerUI之后,或者遗漏了UseAuthorization

错误顺序示例:

app.UseSwagger();
app.UseSwaggerUI();
app.UseAuthentication();
app.UseAuthorization();

正确顺序应为:

app.UseSwagger();
app.UseSwaggerUI();
app.UseAuthentication();
app.UseAuthorization();

但这里有一个陷阱:如果Swagger UI路径(如/swagger)本身没有权限要求,上述顺序正确。然而,若不小心在全局授权过滤器([Authorize])作用域内,则建议将UseSwaggerUI放在UseAuthenticationUseAuthorization之前,以免Swagger UI页面本身也被拦截。

更稳妥的做法是在AddSwaggerGen中配置DocInclusionPredicate或使用UseSwaggerUIRoutePrefix,并确保Swagger端点不应用全局授权。例如在AddSwaggerGen后添加:

builder.Services.AddSwaggerGenNewtonsoftSupport();

(非必需,但可避免JSON序列化冲突)

原因三:全局授权策略导致Swagger端点被锁定

如果在控制器或全局Filter中使用了[Authorize],且未对Swagger中间件路径设置排除规则,那么Swagger UI页面本身和其内部的API请求都可能被拦截。解决方案:在认证配置中跳过Swagger路径。

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters { ... };
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                // 允许Swagger UI不携带token时跳过验证(仅用于UI页面)
                var path = context.HttpContext.Request.Path;
                if (path.StartsWithSegments("/swagger"))
                {
                    context.NoResult = true; // 或者直接return Task.CompletedTask
                }
                return Task.CompletedTask;
            }
        };
    });

注意:这样会允许Swagger UI页面无token访问,但API请求仍需要token。此方法需谨慎使用,更推荐通过UseWhenapp.MapWhen来隔离Swagger路径。

原因四:CORS配置不当导致预检请求失败

若前端(如Swagger UI所在不同域名)发送OPTIONS预检请求未携带Authorization头,但服务器要求认证,则返回401。此时需要在CORS策略中允许Authorization头:

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

并在app.UseCors("AllowAll")后执行认证中间件。

原因五:Swagger版本与认证Scheme不匹配

部分开发者使用AddSwaggerGen时声明了SecuritySchemeType.Http,但JWT认证配置使用AddJwtBearer(默认Scheme名称Bearer)。如果Swagger SecurityDefinition中的Scheme名称与认证Scheme大小写或拼写不一致,可能导致UI发送的token头不被识别。建议统一使用"Bearer"(首字母大写)。

最佳实践清单

  1. 检查中间件顺序UseCorsUseAuthenticationUseAuthorizationUseSwaggerUseSwaggerUI
  2. 确保SecurityRequirement已添加:UI才会自动附加token。
  3. 确认Swagger路径免于全局授权:可使用[AllowAnonymous]特性修饰Swagger控制器,或通过中间件过滤。
  4. 测试Token有效性:用Postman手动添加Authorization: Bearer <token>验证真实接口是否成功,排除Token本身问题。
  5. 查看Swagger生成的curl命令:在Swagger UI中点击“Code”示例,检查请求头中是否包含Authorization。若不包含,则为SecurityRequirement配置遗漏。

结语

ASP.NET Core Swagger一直返回401,绝大多数情况并非框架bug,而是集成配置的“细节魔鬼”。理解中间件管道、安全定义与认证方案的交互逻辑,遵循上述解决方案,开发者就能快速定位问题。建议在项目初期就将Swagger认证集成纳入自动化测试,避免在后续迭代中反复踩坑。随着.NET 8/9对OpenAPI支持的持续增强,未来类似问题有望通过更智能的默认配置得到缓解,但目前掌握这些排查技巧仍是每位.NET后端工程师的必备技能。