【问题标题】:RequireAuthorization and Swashbuckle IOperationFilterRequireAuthorization 和 Swashbuckle IOperationFilter
【发布时间】:2020-06-17 15:23:06
【问题描述】:

我正在寻找一种方法来确定端点是否需要使用 IOperationFilter 进行授权(.Net Core 3.1)。

如果授权是通过过滤器或显式作为属性设置的,则可以在 OperationFilterContext context.ApiDescription.ActionDescriptor.FilterDescriptors.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter)context.ApiDescription.CustomAttributes().OfType<AuthorizeAttribute>() 中找到。

但是如果授权设置为 endpoints.MapControllers().RequireAuthorization();,应该将 AuthorizationAttribute 添加到所有端点,它既不会出现在过滤器中,也不会出现在属性中。如果在这种情况下将身份验证应用于端点,有什么想法吗?

【问题讨论】:

    标签: asp.net-core authorization swashbuckle


    【解决方案1】:

    phwew,我今天能够像这样击败它(swashbuckle 5.63):

    1. 像这样创建一个新类
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.OpenApi.Models;
    using Swashbuckle.AspNetCore.SwaggerGen;
    
    namespace YourNameSpace
    {
       public class SwaggerGlobalAuthFilter : IOperationFilter
       {
          public void Apply( OpenApiOperation operation, OperationFilterContext context )
          {
             context.ApiDescription.TryGetMethodInfo( out MethodInfo methodInfo );
    
             if ( methodInfo == null )
             {
                return;
             }
    
             var hasAllowAnonymousAttribute = false;
    
             if ( methodInfo.MemberType == MemberTypes.Method )
             {
                // NOTE: Check the controller or the method itself has AllowAnonymousAttribute attribute
                hasAllowAnonymousAttribute = 
                   methodInfo.DeclaringType.GetCustomAttributes( true ).OfType<AllowAnonymousAttribute>().Any() ||
                   methodInfo.GetCustomAttributes( true ).OfType<AllowAnonymousAttribute>().Any();
             }
    
             if ( hasAllowAnonymousAttribute )
             {
                return;
             }
    
             // NOTE: This adds the "Padlock" icon to the endpoint in swagger, 
             //       we can also pass through the names of the policies in the List<string>()
             //       which will indicate which permission you require.
             operation.Security = new List<OpenApiSecurityRequirement>
             {
                new OpenApiSecurityRequirement()
                {
                   {
                      new OpenApiSecurityScheme
                      {
                         Reference = new OpenApiReference
                         {
                            Type = ReferenceType.SecurityScheme,
                            Id = "oauth2"  // note this 'Id' matches the name 'oauth2' defined in the swagger extensions config section below
                         },
                         Scheme = "oauth2",
                         Name = "Bearer",
                         In = ParameterLocation.Header,
                      },
                      new List<string>()
                   }
                }
             };
          }
       }
    }
    

    在 swagger 配置扩展中

                options.AddSecurityDefinition( "oauth2", new OpenApiSecurityScheme
                {
                   Type = SecuritySchemeType.OAuth2,
                   Flows = new OpenApiOAuthFlows
                   {
                      Implicit = new OpenApiOAuthFlow
                      {
                        //_swaggerSettings is a custom settings object of our own
                         AuthorizationUrl = new Uri( _swaggerSettings.AuthorizationUrl ),
                         Scopes = _swaggerSettings.Scopes
                      }
                   }
                } );
                options.OperationFilter<SwaggerGlobalAuthFilter>();
    

    将文档、其他 SO 和内置 SecurityRequirementsOperationFilter 的反编译代码放在一起

    afaik,它正在为您的所有路由端点定义全局身份验证设置,除了那些在控制器或端点上明确具有AllowAnonymousAttribute 的端点。因为,正如您最初的问题所暗示的那样,在设置路由时使用扩展名RequireAuthorization() 隐式将该属性放在所有端点上,而检测到Authorize 属性的内置SecurityRequirementsOperationFilter 无法获取它。由于您的路由设置有效地将Authorize 放在每个控制器/路由上,因此设置一个像这样排除AllowAnonymous 的默认全局过滤器似乎与您在管道中配置的内容一致。

    我怀疑可能有一种更“内置”的方式来执行此操作,但我找不到。

    【讨论】:

      【解决方案2】:

      显然,这也是 NSwag repo 上的一个未解决问题(对于像我这样遇到同样问题但使用 NSwag 而不是 Swashbuckle 的人):

      https://github.com/RicoSuter/NSwag/issues/2817

      还有另一个解决问题的例子(不仅是安全要求,还有它的范围)。

      【讨论】:

        【解决方案3】:

        我知道这个问题已经很久没有提出来了。

        但我遇到了类似的问题,并按照 GitHub here 中的问题的建议,设法使用 IOperationFilter 的这个实现来解决它(现在就像一个魅力):

            public class AuthorizeCheckOperationFilter : IOperationFilter
            {
                private readonly EndpointDataSource _endpointDataSource;
        
                public AuthorizeCheckOperationFilter(EndpointDataSource endpointDataSource)
                {
                    _endpointDataSource = endpointDataSource;
                }
        
        
                public void Apply(OpenApiOperation operation, OperationFilterContext context)
                {
                    var descriptor = _endpointDataSource.Endpoints.FirstOrDefault(x =>
                        x.Metadata.GetMetadata<ControllerActionDescriptor>() == context.ApiDescription.ActionDescriptor);
                    
                    var hasAuthorize = descriptor.Metadata.GetMetadata<AuthorizeAttribute>()!=null;
        
                    var allowAnon = descriptor.Metadata.GetMetadata<AllowAnonymousAttribute>() != null;
        
                    if (!hasAuthorize || allowAnon) return;
        
                    operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
                    operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
        
                    operation.Security = new List<OpenApiSecurityRequirement>
                    {
                        new()
                        {
                            [
                                new OpenApiSecurityScheme {Reference = new OpenApiReference
                                    {
                                        Type = ReferenceType.SecurityScheme,
                                        Id = "oauth2"}
                                }
                            ] = new[] {"api1"}
                        }
                    };
                }
            }
        

        问题说明了这一点:

        ControllerActionDescriptor.EndpointMetadata 只反映元数据 在控制器动作上发现。通过 端点 API 不会在此处显示。这主要是我们的原因 将其记录为仅基础设施,因为它有点令人困惑 使用。

        您可以使用几个选项

        a) 你可以使用 [Authorize] 来装饰你的控制器。这应该允许元数据显示在属性中。

        b) 您可以通过读取 EndpointDataSource 来查找元数据。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-13
          • 2020-08-03
          • 2017-01-02
          • 1970-01-01
          相关资源
          最近更新 更多