web-dev-qa-db-ja.com

承認を使用するようにSwashBuckle.AspNetCore.Swaggerを設定するにはどうすればよいですか?

Swashbuckle.AspNetCore.Swaggerを使用してAPIを文書化しました。swaggeruiを使用して、Authorize属性を持つリソースをテストします。

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

応答コードは無許可401ですが、swaggerを使用してこれをどのように認証できますか?

IdentityServer4を使用して認証サーバーをセットアップしています。

承認サーバー-startup.cs

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

承認サーバー-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();
    }

ログイン画面にリダイレクトし、ユーザーが権限を持つAPIリソースへのアクセスを許可する承認ボタンが必要です。これを行うためにasp.net core 1.1 Swaggerミドルウェアを使用することは可能ですか?または、IdentityServer4承認サーバーからトークンを取得するjavascriptを記述する必要がありますか?私は認証と承認に不慣れなので、助けてください

12
S.Anaconda

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"}
                },
    ...
    ...
    ...
   }
}

承認が必要なメソッドの横に赤い感嘆符アイコンが表示されるように、APIでSwagger OperationFilterを作成しました

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

完了するには、oauth2セキュリティ定義とoperationfilterを追加してswaggerで承認を構成しました

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" }
                    }
                });
            });
10
S.Anaconda

承認された回答へのコメントでジェームズが述べたように、Authorize属性を確認する方法は少し異なります。回答のAuthorizeCheckOperationFilterを少し調整する必要があります。これは、これを行うための100%の最善の方法ではないかもしれません。ただし、以下のコードには問題がありませんでした。

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[] { } }
        });
    }
}
6
Dan