Authorization for JWT bearer in Swashbuckle .NET Core 2

吃可爱长大的小学妹 提交于 2019-11-30 15:14:10

问题


I use tokens generated by an authentication service for my app. No problems there. Now I have introduced Swashbuckle to document my API an I can authenticate as follows by sending the JWT with every request using this code;

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 = "a@example.com", 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"
            });
        }
    }
}

Which gives me the following header - as expected

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

Although, I am not sure where the Cookie is coming from. That is nothing to do with my code. I just ignore it - so far so good.

The problem is that this means the token has to be entered with every request which is a pain. Ideally, I would want to authenticate using the inbuilt swagger interface - according to several articles, I should be able to do this;

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

This works fine, and I can add the token, there just seems to be a step I am missing to add the token to the header of every request. If I just add the auth, then this gives me the following header, which of course fails the authentication.

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

What else do I need to do in order to get the request to include the token for every subsequent request?


回答1:


Swagger would add the authorzation header if you specified the filter on your methods. If you globally require authorization my guess is that swagger doesn't recognize them.

You need to add a SecurityRequirement like this in your ConfigureServices:

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

This will require the header to be sent with every request if the token is set. If you didn't set the header before it'll not send it, but you'll still have the padlock sign next to your api description.




回答2:


If you define in code

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

and then use it in not in .Parameters, but in .Security

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

then everything should work:

I do the same as you do, but you should add like follows (for oauth2 or jwt bearer token auth):

    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 }
                }
            };
            }
        }
    }
}

Usage:

        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" } }
        );


来源:https://stackoverflow.com/questions/48985240/authorization-for-jwt-bearer-in-swashbuckle-net-core-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!