![](/img/trans.png)
[英]In Swashbuckle.AspNetCore, Swagger JSON and UI generated without content type when Results.Ok is used
[英]Bearer authentication in Swagger UI, when migrating to Swashbuckle.AspNetCore version 5
我正在嘗試在 .NET Core 3 Preview 5 Web API 項目中從 Swashbuckle 的 4.0.1 版本遷移到 5.0.0-rc2。
我已經編譯了項目並且 Swagger UI 工作了,但是我無法讓 Bearer 身份驗證工作,我認為這是因為我沒有正確設置新的格式安全性。
這是我在版本 4 中工作的舊代碼:
c.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
Name = "Authorization",
In = "header",
Type = "apiKey"
});
var security = new Dictionary<string, IEnumerable<string>>
{
{"Bearer", new string[] { }},
};
c.AddSecurityRequirement(security);
這就是我為 v5 更改的內容:
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "tomsAuth"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "tomsAuth" }
}, new List<string>() }
});
我認為我的問題可能出在這部分代碼中:
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "tomsAuth" }
}, new List<string>() }
我認為那個位可能應該在某個地方有“承載”,但我不確定在哪里?
這就是我首先設置 JWT 身份驗證的方式。 當我使用 Swashbuckle 4.0.1 時,此代碼沒有更改並且可以正常工作:
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<IApiUserService>();
var userId = int.Parse(context.Principal.Identity.Name);
var user = userService.GetById(userId);
if (user == null)
{
// return unauthorized if user no longer exists
context.Fail("Unauthorized");
}
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
通過反復試驗最終得到了這個工作。 這是對我有用的代碼:
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description =
"JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
我懷疑那里可能設置了實際上不需要顯式設置的屬性,但以上內容對我有用。
OpenAPI 3.0 自帶 Bearer 認證,這是一個安全方案,類型為:http,scheme:bearer。
因此,您必須將安全方案類型設置為 HTTP 身份驗證,然后按照RFC7235中的定義定義 HTTP 授權方案的名稱,而不是使用 API 密鑰方案。 在這種情況下,“承載者”。
定義安全方案后,您可以通過將其添加為安全要求來應用它。
//First we define the security scheme
c.AddSecurityDefinition("Bearer", //Name the security scheme
new OpenApiSecurityScheme{
Description = "JWT Authorization header using the Bearer scheme.",
Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
Scheme = "bearer" //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "Bearer", //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
這省略了在令牌前面加上“Bearer”的需要。
這些答案對我一路走來很有幫助。 就我而言,我總是錯過了另一件事——在使用[Authorize]
裝飾動作/控制器時,SwaggerUI 沒有將我選擇的標頭名稱/值 (X-API-KEY) 傳遞給我的身份驗證處理程序。 我的項目使用 .NET Core 3.1 和 Swashbuckle 5。我創建了一個繼承IOperationFilter
的自定義類,該類使用下面的Swashbuckle.AspNetCore.Filters
nuget 包來捎帶他們對 oauth2 的實現。
// Startup.cs
// ...
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = nameof(BoardMinutes), Version = "v1" });
// Adds authentication to the generated json which is also picked up by swagger.
options.AddSecurityDefinition(ApiKeyAuthenticationOptions.DefaultScheme, new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Name = ApiKeyAuthenticationHandler.ApiKeyHeaderName,
Type = SecuritySchemeType.ApiKey
});
options.OperationFilter<ApiKeyOperationFilter>();
});
關鍵組件是options.AddSecurityDefinition()
(我有一些開放的端點,不想提供全局過濾器)以及options.OperationFilter<ApiKeyOperationFilter>()
。
// ApiKeyOperationFilter.cs
// ...
internal class ApiKeyOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// Piggy back off of SecurityRequirementsOperationFilter from Swashbuckle.AspNetCore.Filters which has oauth2 as the default security scheme.
var filter = new SecurityRequirementsOperationFilter(securitySchemaName: ApiKeyAuthenticationOptions.DefaultScheme);
filter.Apply(operation, context);
}
}
最后 - 對於完整的圖片,這里是身份驗證處理程序和身份驗證選項
// ApiKeyAuthenticationOptions.cs
// ...
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public const string DefaultScheme = "API Key";
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
}
// ApiKeyAuthenticationHandler.cs
// ...
internal class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
private const string ProblemDetailsContentType = "application/problem+json";
public const string ApiKeyHeaderName = "X-Api-Key";
private readonly IApiKeyService _apiKeyService;
private readonly ProblemDetailsFactory _problemDetailsFactory;
public ApiKeyAuthenticationHandler(
IOptionsMonitor<ApiKeyAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IApiKeyService apiKeyService,
ProblemDetailsFactory problemDetailsFactory) : base(options, logger, encoder, clock)
{
_apiKeyService = apiKeyService ?? throw new ArgumentNullException(nameof(apiKeyService));
_problemDetailsFactory = problemDetailsFactory ?? throw new ArgumentNullException(nameof(problemDetailsFactory));
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKeyHeaderValues))
{
return AuthenticateResult.NoResult();
}
Guid.TryParse(apiKeyHeaderValues.FirstOrDefault(), out var apiKey);
if (apiKeyHeaderValues.Count == 0 || apiKey == Guid.Empty)
{
return AuthenticateResult.NoResult();
}
var existingApiKey = await _apiKeyService.FindApiKeyAsync(apiKey);
if (existingApiKey == null)
{
return AuthenticateResult.Fail("Invalid API Key provided.");
}
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, existingApiKey.Owner)
};
var identity = new ClaimsIdentity(claims, Options.AuthenticationType);
var identities = new List<ClaimsIdentity> { identity };
var principal = new ClaimsPrincipal(identities);
var ticket = new AuthenticationTicket(principal, Options.Scheme);
return AuthenticateResult.Success(ticket);
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.StatusCode = StatusCodes.Status401Unauthorized;
Response.ContentType = ProblemDetailsContentType;
var problemDetails = _problemDetailsFactory.CreateProblemDetails(Request.HttpContext, StatusCodes.Status401Unauthorized, nameof(HttpStatusCode.Unauthorized),
detail: "Bad API key.");
await Response.WriteAsync(JsonSerializer.Serialize(problemDetails));
}
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.StatusCode = StatusCodes.Status403Forbidden;
Response.ContentType = ProblemDetailsContentType;
var problemDetails = _problemDetailsFactory.CreateProblemDetails(Request.HttpContext, StatusCodes.Status403Forbidden, nameof(HttpStatusCode.Forbidden),
detail: "This API Key cannot access this resource.");
await Response.WriteAsync(JsonSerializer.Serialize(problemDetails));
}
}
找到解決方案后,我創建了一個示例 Swagger-UI ASP.NET 6 應用程序,當您單擊“授權”按鈕時,從身份驗證提供程序請求不記名令牌。 見https://github.com/inouiw/SwaggerUIJsonWebToken
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.