web-dev-qa-db-ja.com

Swashbuckle .NET Core 2でのJWTベアラーの承認

アプリの認証サービスで生成されたトークンを使用しています。そこに問題はありません。このコードを使用してすべてのリクエストでJWTを送信することにより、次のように認証できるAPIを文書化するためにSwashbuckleを導入しました。

services.AddSwaggerGen(c =>
{
    var a = new ApiKeyScheme();
    //c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
    //{ In = "header", Description = "Please insert JWT with Bearer into field", Name = "Authorization", Type = "apiKey" });

    c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();

    c.SwaggerDoc("v2", new Info
    {
        Version = "v2",
        Title = "MyTitle",
        Description = "An interface for ...",
        TermsOfService = "None",
        Contact = new Contact() { Name = "MyApp", Email = "[email protected]", Url = "www.example.com" }
    });
    // Set the comments path for the Swagger JSON and UI.
    var basePath = AppContext.BaseDirectory;
    var xmlPath = Path.Combine(basePath, "cpDataCore.xml");
    c.IncludeXmlComments(xmlPath);
});

public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
        var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
        var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);

        if (isAuthorized && !allowAnonymous)
        {
            if (operation.Parameters == null)
                operation.Parameters = new List<IParameter>();

            operation.Parameters.Add(new NonBodyParameter
            {
                Name = "Authorization",
                In = "header",
                Description = "access token",
                Required = true,
                Type = "string"
            });
        }
    }
}

これは私に次のヘッダーを与えます-期待通り

accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:en-AU,en;q=0.9
Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJEZW5uaXMiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zdXJuYW1lIjoiR2FzY29pZ25lIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6ImRlbm5pc2ciLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiI1NCIsIlJlZnJlc2hUb2tlbiI6IjY5OTA1NTFmLTNhOTQtNDVmYi1hYjc2LTZlOTQyNGE3NjJmOCIsIkFsbERhdGFSZWFkT25seUZvckFwcHJvdmVycyI6IlRydWUiLCJQcm9qZWN0SUQiOiI2IiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoic3lzYWRtaW4iLCJuYmYiOjE1MTk2MzY2NDIsImV4cCI6MTUxOTYzODQ0MiwiaXNzIjoiaHR0cHM6Ly9kYXRhLmNpdmlscHJvc29mdHdhcmUuY29tLyIsImF1ZCI6Imh0dHBzOi8vcm1zLmNpdmlscHJvc29mdHdhcmUuY29tLyJ9.nBEZgzcmZVGhFJmKI8u7p7g7xPU13HEAGJu_lrWylnc
Connection:keep-alive
Cookie:username=demo; jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJUcm95IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc3VybmFtZSI6IkVsZGVyIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6InRyb3kiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiI1IiwiUmVmcmVzaFRva2VuIjoiMTNhNzRmNDQtNmVmOC00MDQ3LTlmYWYtOWQ3MzI4MmNhZjQ4IiwiUHJvamVjdElEIjoiLTEiLCJuYmYiOjE1MDUwOTc3MjEsImV4cCI6MTUwNTA5ODYyMSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo2MDAwMC8iLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjYwMDAwLyJ9.8You0XiUlvdHb2TRuDzaiXv6r74v7ga1Av_Z3ikmblU
Host:localhost:60000
Referer:http://localhost:60000/swagger/
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36

ただし、Cookieの送信元はわかりません。それは私のコードとは何の関係もありません。私はそれを無視します-これまでのところとても良いです。

問題は、これは、すべての要求でトークンを入力する必要があることを意味し、これは面倒です。理想的には、組み込みのswaggerインターフェースを使用して認証したいのですが、いくつかの記事によると、これを実行できるはずです。

c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
{ In = "header", Description = "Please insert JWT with Bearer into field", Name = "Authorization", Type = "apiKey" });

これは正常に機能し、トークンを追加できます。トークンをすべてのリクエストのヘッダーに追加するために欠けているステップがあるようです。単に認証を追加すると、次のヘッダーが表示されますが、これはもちろん認証に失敗します。

GET /api/ApprovalItemTypes HTTP/1.1
Host: localhost:60000
Connection: keep-alive
accept: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36
Referer: http://localhost:60000/swagger/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-AU,en;q=0.9
Cookie: username=demo; jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW5_xxx__LTEiLCJuYmYiOjE1MDUwOTc3MjEsImV4cCI6MTUwNTA5ODYyMSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo2MDAwMC8iLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjYwMDAwLyJ9.8You0XiUlvdHb2TRuDzaiXv6r74v7ga1Av_Z3ikmblU

リクエストに後続のすべてのリクエストのトークンを含めるには、他に何が必要ですか?

10
statler

メソッドでフィルターを指定した場合、Swaggerはauthorzationヘッダーを追加します。あなたが全体的に承認を必要とするなら、私の推測はswaggerがそれらを認識しないことだと思います。

ConfigureServicesに次のようなSecurityRequirementを追加する必要があります。

c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>()
{
  { "Bearer", new string[]{ } }
});

これには、トークンが設定されている場合、ヘッダーがeveryリクエストで送信される必要があります。ヘッダーを設定していない場合は送信されませんが、APIの説明の横に南京錠の記号が表示されます。

1
Agash Thamo.

結局、NSwagに移動したので、元の問題が何であったかはわかりません。 Nswagでの解決策は次のとおりです。

    services.AddSwaggerDocument(document =>
        {
            document.DocumentName = "CPSwagger";
            document.Title = "My Mobile API";
            document.Version = "v10.3.4";
            document.Description = "An interface for some software.";

            document.DocumentProcessors.Add(
                new SecurityDefinitionAppender("JWT token", new NSwag.OpenApiSecurityScheme
                {
                    Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
                    Name = "Authorization",
                    Description = "Copy 'Bearer ' + valid JWT token into field",
                    In = NSwag.OpenApiSecurityApiKeyLocation.Header
                }));
            document.OperationProcessors.Add(new OperationSecurityScopeProcessor("JWT token"));
        });
0
statler

コードで定義した場合

c.AddSecurityDefinition("jwt", new ApiKeyScheme()
{ 
   In = "header", Description = "Please insert JWT with Bearer into field", Name = "Authorization", Type = "apiKey" });

そしてそれを.Parametersではなく.Securityで使用します

operation.Security = new List<IDictionary<string, IEnumerable<string>>> {
            new Dictionary<string, IEnumerable<string>>
            {
                {"jwt", _scopes }
            }

その後、すべてが機能するはずです:

私はあなたと同じですが、次のように追加する必要があります(oauth2またはjwtベアラートークン認証の場合):

    public static class ServiceCollectionExtension
{
    private static string XmlCommentsFilePath
    {
        get
        {
            var basePath = PlatformServices.Default.Application.ApplicationBasePath;
            var fileName = Assembly.GetEntryAssembly().GetName().Name + ".xml";
            return Path.Combine(basePath, fileName);
        }
    }

    public static void AddMySwagger(
        this IServiceCollection services,
        ApiVersion defaultApiVersion,
        Func<ApiVersionDescription, Info> info,
        string authority = null,
        Dictionary<string, string> scopes = null)
    {
        services.AddMvcCore().AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV");

        services.AddApiVersioning(o =>
        {
            o.ReportApiVersions = true;
            o.AssumeDefaultVersionWhenUnspecified = true;
            o.DefaultApiVersion = defaultApiVersion;
        });

        services.AddSwaggerGen(
            options =>
            {
                var provider = services.BuildServiceProvider()
                    .GetRequiredService<IApiVersionDescriptionProvider>();

                foreach (var description in provider.ApiVersionDescriptions)
                {
                    if (!description.IsDeprecated)
                        options.SwaggerDoc(description.GroupName, info(description));
                }

                options.OperationFilter<DefaultValues>();

                options.IncludeXmlComments(XmlCommentsFilePath);

                if (!string.IsNullOrEmpty(authority))
                {
                    options.AddSecurityDefinition("jwt", new ApiKeyScheme()
                    {
                        Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
                        Name = "Authorization",
                        In = "header",
                        Type = "apiKey"
                    });
                    //options.AddSecurityDefinition("oauth2", new OAuth2Scheme
                    //{
                    //  Flow = "implicit",
                    //  AuthorizationUrl = $"{authority}/connect/authorize",
                    //  Scopes = scopes ?? new Dictionary<string, string>()
                    //});
                    options.OperationFilter<AuthorizeCheckOperationFilter>(scopes?.Select(_ => _.Key).ToList() ?? new List<string>());
                }
            });
    }

    class AuthorizeCheckOperationFilter : IOperationFilter
    {
        private readonly IEnumerable<string> _scopes;

        public AuthorizeCheckOperationFilter(IEnumerable<string> scopes)
        {
            _scopes = scopes;
        }

        public void Apply(Operation operation, OperationFilterContext context)
        {
            var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType<AuthorizeAttribute>().Any() ||
                               context.ApiDescription.ActionAttributes().OfType<AuthorizeAttribute>().Any();

            if (hasAuthorize)
            {
                operation.Responses.Add("401", new Response { Description = "Unauthorized" });
                operation.Responses.Add("403", new Response { Description = "Forbidden" });

                operation.Security = new List<IDictionary<string, IEnumerable<string>>> {
                new Dictionary<string, IEnumerable<string>>
                {
                    //{"oauth2", _scopes},
                    {"jwt", _scopes }
                }
            };
            }
        }
    }
}

使用法:

        services.AddMySwagger(
            new ApiVersion(1, 0),
            __description => new Info { Title = $"API v{__description.ApiVersion}", Version = __description.ApiVersion.ToString() },
            Configuration.GetValue<string>("Authentication:Authority"),
            new Dictionary<string, string> { { Configuration.GetValue<string>("Authentication:Scope"), "Partnership API" } }
        );
0
Artur Mustafin