【问题标题】:How can I setup SwashBuckle.AspNetCore.Swagger to use Authorization?如何设置 SwashBuckle.AspNetCore.Swagger 以使用授权?
【发布时间】:2018-01-28 23:22:14
【问题描述】:

我已经使用 Swashbuckle.AspNetCore.Swagger 记录了我的 api,我想使用 swagger ui 测试一些具有 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 });
            }
        }
    }

响应码是 Unauthorized 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 - 启动.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? 请帮忙,因为我是身份验证和授权的新手

【问题讨论】:

标签: c# asp.net-core swagger swagger-ui identityserver4


【解决方案1】:

对于 .Net Core 和 Swashbuckle.AspNetCore 5.4.1,以下是 @Dan 回复的工作更新:

创建一个 IOperationalFilter 类:

internal class AuthorizeCheckOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation 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;

        if (!operation.Responses.Any(r => r.Key == StatusCodes.Status401Unauthorized.ToString()))
            operation.Responses.Add(StatusCodes.Status401Unauthorized.ToString(), new OpenApiResponse { Description = "Unauthorized" });
        if (!operation.Responses.Any(r => r.Key == StatusCodes.Status403Forbidden.ToString()))
            operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new OpenApiResponse { 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<OpenApiSecurityRequirement>
        {
            new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        },
                        Scheme = "oauth2",
                        Name = "Bearer",
                        In = ParameterLocation.Header
                    },
                    new List<string>()
                }
            }
        };
    }
}

在 Startup 中连接过滤器和安全定义:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSwaggerGen(c =>
    {
        c.OperationFilter<AuthorizeCheckOperationFilter>();
        c.EnableAnnotations();
        c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Scheme = "Bearer",
            In = ParameterLocation.Header,
            Name = "Authorization",
            Type = SecuritySchemeType.ApiKey,
            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'"
        });
    });
    ...
}

【讨论】:

    【解决方案2】:

    正如 James 在对已接受答案的评论中所提到的,现在检查 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[] { } }
            });
        }
    }
    

    【讨论】:

    • 此答案使用 Swashbuckle.AspNetCore (4.0.1) 效果很好,感谢您花时间保持最新状态。
    • 请注意,过滤器不适用于旧版本的 Swashbuckle.AspNetCore,因为它们没有 TryGetMethodInfo 扩展。
    【解决方案3】:

    我通过向 IdentityServer4 授权服务器项目添加一个新客户端解决了这个问题。

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

    我在 tha API 中创建了一个大摇大摆的 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 安全定义和操作过滤器在 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" }
                        }
                    });
                });
    

    【讨论】:

    • 这个现在已经贬值了,我不知道如何修复它。想法? var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType().Any() || context.ApiDescription.ActionAttributes().OfType().Any()
    猜你喜欢
    • 2011-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-24
    相关资源
    最近更新 更多