简体   繁体   English

如何在 ASP.Net Core (WebAPI) 中基于 HTTP 动词和其他属性以声明方式指定授权策略

[英]How to declaritively specify authorization policy based on HTTP verb and other attributes in ASP.Net Core (WebAPI)

I've got a couple of authorization polcies registered:我已经注册了几个授权策略:

ConfigureServices()
{
    services.AddAuthorization(authorisationOptions =>
    {
        authorisationOptions.AddPolicy(StandardAuthorizationPolicy.Name, StandardAuthorizationPolicy.Value);
        authorisationOptions.AddPolicy(MutatingActionAuthorizationPolicy.Name, MutatingActionAuthorizationPolicy.Value);
    }); 
}

& then I set a default authorization policy across all endpoints: & 然后我在所有端点上设置默认授权策略:

Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseEndpoints(endpoints =>
    {
        endpoints
            .MapControllers()
            .RequireAuthorization(StandardAuthorizationPolicy.Name); // Declaratively require the standard authorization policy on all controller endpoints
    });
}

On the endpoints where I want to specify the mutating policy, I currently do the following:在我要指定变异策略的端点上,我目前执行以下操作:

[HttpPut]
[Authorize(MutatingActionAuthorizationPolicy.Name)] // Because 'PUT'. NOT DECLARATIVE! :-(
public async Task<IActionResult> AddOrUpdateOverride(SourceOverride sourceOverride, CancellationToken cancellationToken)
{
  // ..
}

What I really want is a bit more control to declaritively apply the mutating policy based on the HttpVerb (ie POST, PUT, PATCH, DELETE).我真正想要的是更多的控制,以声明性地应用基于 HttpVerb 的变异策略(即 POST、PUT、PATCH、DELETE)。

Any idea on how to achieve that?关于如何实现这一目标的任何想法? Bonus points for allowing me to use other attributes on the controller method/class, not just [HttpPost] etc.允许我在 controller 方法/类上使用其他属性的奖励积分,而不仅仅是[HttpPost]等。

NB: I've seen solutions floating around that involve casting the content (and seem to revolve around a single access policy).注意:我已经看到涉及投射内容的解决方案(并且似乎围绕单一访问策略)。 I'd really rather stick with multiple access policies.我真的宁愿坚持使用多种访问策略。

If I get stuck, I might end up writing a convention test for it.如果我被卡住了,我可能最终会为它编写一个约定测试。

You can implement a custom RequireAuthorization extension that takes HTTP verb filtering function as an argument and checks each endpoint metadata for HttpMethodAttribute您可以实现一个自定义 RequireAuthorization 扩展,该扩展采用 HTTP 动词过滤 function 作为参数,并检查每个端点元数据中的 HttpMethodAttribute

public static class AuthorizationEndpointConventionBuilderExtensions
    {
        /// <summary>
        /// Adds authorization policies with the specified <see cref="IAuthorizeData"/> to the endpoint(s) filtered by supplied filter function
        /// </summary>
        /// <param name="builder">The endpoint convention builder.</param>
        /// <param name="filterOnHttpMethods">Filters http methods that we applying specific policies to</param>
        /// <param name="authorizeData">
        /// A collection of <paramref name="authorizeData"/>. If empty, the default authorization policy will be used.
        /// </param>
        /// <returns>The original convention builder parameter.</returns>
        public static TBuilder RequireAuthorizationForHttpMethods<TBuilder>(this TBuilder builder, Func<IEnumerable<HttpMethod>, bool> filterOnHttpMethods, params IAuthorizeData[] authorizeData)
            where TBuilder : IEndpointConventionBuilder
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (authorizeData == null)
            {
                throw new ArgumentNullException(nameof(authorizeData));
            }

            if (authorizeData.Length == 0)
            {
                authorizeData = new IAuthorizeData[] { new AuthorizeAttribute(), };
            }

            builder.Add(endpointBuilder =>
            {
                var appliedHttpMethodAttributes = endpointBuilder.Metadata
                .Where(x => x is HttpMethodAttribute)
                .Cast<HttpMethodAttribute>();

                if (appliedHttpMethodAttributes.Any(x => filterOnHttpMethods(x.HttpMethods
                                              .Select(method => new HttpMethod(method)))))
                {
                    foreach (var data in authorizeData)
                    {
                        endpointBuilder.Metadata.Add(data);
                    }
                }
            });
            return builder;
        }

        /// <summary>
        /// Adds authorization policies with the specified names to the endpoint(s) for filtered endpoints that return for filterOnHttpMethod
        /// </summary>
        /// <param name="builder">The endpoint convention builder.</param>
        /// <param name="filterOnHttpMethods">Filters http methods that we applying specific policies to</param>
        /// <param name="policyNames">A collection of policy names. If empty, the default authorization policy will be used.</param>
        /// <returns>The original convention builder parameter.</returns>
        public static TBuilder RequireAuthorizationForHttpMethods<TBuilder>(this TBuilder builder, Func<IEnumerable<HttpMethod>, bool> filterOnHttpMethods, params string[] policyNames)
        where TBuilder : IEndpointConventionBuilder
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            if (policyNames == null)
            {
                throw new ArgumentNullException(nameof(policyNames));
            }

            return builder.RequireAuthorizationForHttpMethods(filterOnHttpMethods, policyNames.Select(n => new AuthorizeAttribute(n)).ToArray());
        }
    }

And then use this extension next to the original one:然后在原始扩展旁边使用此扩展:

        app.UseEndpoints(endpoints =>
        {
            var mutatingHttpMethods = new HashSet<HttpMethod>()
            {
                HttpMethod.Post,
                HttpMethod.Put,
                HttpMethod.Delete
            };

            endpoints
                .MapControllers()
                .RequireAuthorization(StandardAuthorizationPolicy.Name)
                .RequireAuthorizationForHttpMethods(httpMethods => 
                 httpMethods.Any(httpMethod => mutatingHttpMethods.Contains(httpMethod)), 
                 MutatingActionAuthorizationPolicy.Name);
            });
        }


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM