web-dev-qa-db-fra.com

Comment puis-je configurer SwashBuckle.AspNetCore.Swagger pour utiliser l'autorisation?

J'ai documenté mon api en utilisant Swashbuckle.AspNetCore.Swagger et je souhaite tester certaines ressources dotées de l'attribut Autoriser en utilisant swagger ui.

api

 using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using System.Linq;

    namespace Api.Controllers
    {
        [Route("[controller]")]
        [Authorize]
        public class IdentityController : ControllerBase
        {
            [HttpGet]
            public IActionResult Get()
            {
                return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
            }
        }
    }

 swagger ui

Le code de réponse est Unauthorized 401, alors comment puis-je l'autoriser en utilisant swagger?

J'ai une configuration de serveur d'autorisation utilisant IdentityServer4.

serveur d'autorisation - startup.cs

services.AddIdentityServer()
        .AddTemporarySigningCredential()
        .AddInMemoryPersistedGrants()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddAspNetIdentity<ApplicationUser>();

serveur d'autorisation - config.cs

    public class Config
{
    // scopes define the resources in your system
    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
        };
    }

    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("api1", "My API")
        };
    }
   ...
   ...
}

api - startup.cs

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
        ECommerceDbContext context)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
        {
            Authority = "http://localhost:5000/",
            RequireHttpsMetadata = false,
            AutomaticAuthenticate = true,
            ApiName = "api1"
        });


        // Enable middleware to serve generated Swagger as a JSON endpoint.
        app.UseSwagger();

        // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        });

        DbInitialiser.Init(context);

        app.UseMvc();
    }

Je souhaite un bouton d'autorisation qui redirige vers un écran de connexion, puis accorde l'accès aux ressources de l'API pour lesquelles l'utilisateur dispose d'autorisations. Est-il possible d'utiliser le middleware asp.net core 1.1 Swagger Core pour ce faire? Ou dois-je écrire du javascript qui obtient un jeton du serveur d'autorisations IdentityServer4? S'il vous plaît, aidez-moi car je suis nouveau en authentification et autorisation

7
S.Anaconda

J'ai résolu ce problème en ajoutant un nouveau client au projet IdentityServer4 Authorization Server.

config.cs

 // clients want to access resources (aka scopes)
        public static IEnumerable<Client> GetClients()
        {
            // client credentials client
            return new List<Client>
            {
                new Client
                {
                    ClientId="swaggerui",
                    ClientName = "Swagger UI",
                    AllowedGrantTypes=GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser=true,
                    RedirectUris = { "http://localhost:49831/swagger/o2c.html" },
                    PostLogoutRedirectUris={ "http://localhost:49831/swagger/" },
                    AllowedScopes = {"api1"}
                },
    ...
    ...
    ...
   }
}

J'ai créé un swagger OperationFilter dans cette API afin qu'une icône en forme de point d'exclamation rouge apparaisse à côté de la méthode nécessitant une autorisation.

internal class AuthorizeCheckOperationFilter : IOperationFilter
    {
        public void Apply(Operation operation, OperationFilterContext context)
        {
            // Check for authorize attribute
            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>>>();
                operation.Security.Add(new Dictionary<string, IEnumerable<string>>
            {
                { "oauth2", new [] { "api1" } }
            });
            }
        }
    }

Pour finir, j'ai configuré l'autorisation dans swagger en ajoutant une définition de sécurité et un filtre de contrôle oauth2

startup.cs

 services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info
                {
                    Version = "v1",
                    Title = "ECommerce API",
                    Description = "",
                    TermsOfService = "None",
                    Contact = new Contact { Name = "", Email = "", Url = "" },
                    License = new License { Name = "", Url = "" }
                });

                //Set the comments path for the swagger json and ui.
                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "WebApi.xml");
                c.IncludeXmlComments(xmlPath);

                c.OperationFilter<AuthorizeCheckOperationFilter>();

                c.AddSecurityDefinition("oauth2", new OAuth2Scheme
                {
                    Type = "oauth2",
                    Flow = "implicit",
                    AuthorizationUrl = "http://localhost:5000/connect/authorize",
                    TokenUrl = "http://localhost:5000/connect/token",
                    Scopes = new Dictionary<string, string>()
                    {
                        { "api1", "My API" }
                    }
                });
            });
6
S.Anaconda

Comme mentionné par James dans le commentaire de la réponse acceptée, la manière de vérifier l'attribut Autoriser est légèrement différente maintenant, la variable AuthorizeCheckOperationFilterdans la réponse doit être légèrement modifiée, cela peut ne pas être à 100% la meilleure façon de le faire, mais je n'ai pas encore eu des problèmes avec le code ci-dessous.

internal class AuthorizeCheckOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        context.ApiDescription.TryGetMethodInfo(out var methodInfo);

        if (methodInfo == null)
             return;

        var hasAuthorizeAttribute = false;

        if (methodInfo.MemberType == MemberTypes.Method)
        {
            // NOTE: Check the controller itself has Authorize attribute
            hasAuthorizeAttribute = methodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();

            // NOTE: Controller has Authorize attribute, so check the endpoint itself.
            //       Take into account the allow anonymous attribute
            if (hasAuthorizeAttribute)
                hasAuthorizeAttribute = !methodInfo.GetCustomAttributes(true).OfType<AllowAnonymousAttribute>().Any();
            else
                hasAuthorizeAttribute = methodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
        }

        if (!hasAuthorizeAttribute)
            return;

        operation.Responses.Add(StatusCodes.Status401Unauthorized.ToString(), new Response { Description = "Unauthorized" });
        operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new Response { Description = "Forbidden" });

        // NOTE: This adds the "Padlock" icon to the endpoint in swagger, 
        //       we can also pass through the names of the policies in the string[]
        //       which will indicate which permission you require.
        operation.Security = new List<IDictionary<string, IEnumerable<string>>>();
        operation.Security.Add(new Dictionary<string, IEnumerable<string>>
        {
            { "Bearer", new string[] { } }
        });
    }
}
1
Dan