授权将索赔与当前请求进行比较

时间:2020-06-17 16:03:59

标签: c# asp.net-core .net-core razor-pages

我找不到简单的方法来做应该很普遍的事情。

我有Razor页面,我想添加一些授权逻辑以确保请求

https://localhost:5001/admin/subscriptions/123`

123是任何Guid的地方是{subscriptionId}

我使用的是基于cookie的身份验证,因此对于当前请求,我应该有一个声明Name或类似的声明,并带有一个特定的subscriptionId,例如124

我想处理这些订阅,以便在URL中的subscriptionId与特定声明不匹配时禁止访问。

这很尴尬,但我不知道该如何使用AspNet Core 3.1和Razor页面。

似乎根本不是针对此类情况设计的。我发现的示例认为,我们已经事先知道了索赔应该是什么。例如,在Microsoft官方文档中,它显示了如何将“年龄”声明与硬编码魔术int 21的最小年龄进行比较。

如果两个值都依赖于请求怎么办?

当我添加

services
                .AddAuthorization(
                    options =>
                    {
                        options.AddPolicy(
                            "SameSubscriptionIdPolicy",
                            policy =>
                                policy.RequireAssertion(context =>
                                {
                                    var claims = context.User.Claims;
                                    var subscriptionId = context.User.FindFirst(x => x.Type == ClaimTypes.Name);
                                    // how can I access here the Request and do the comparison?
                                }));
                    });

如果访问上下文,则会看到参数化的URL,而不是请求的真实值。 我看到admin/subscriptions/{subscriptionId}而不是诸如admin/subscriptions/124

之类的特定值

如何访问URL查询?,以便可以将其与声明值进行比较?


更新1:

我在某处读过here关于这种铸造的内容:

context.Resource is AuthorizationFilterContext mvcContext

那是行不通的。资源是AuthorizationHandlerContext

我也尝试了以下方法

services
    .AddAuthorization(
        options =>
        {
            options.AddPolicy(
                "SameSubscriptionIdPolicy",
                policy =>
                    policy.RequireAssertion(context =>
                    {
                        var claims = context.User.Claims;
                        var subscriptionId = context.User.FindFirst(x => x.Type == ClaimTypes.Name);
                        var accessor = new HttpContextAccessor();
                        var url = accessor.HttpContext.GetRouteData();
                        return false;
                    }));
        });

HttpContext 为空。也许到政策启动时,还没有任何请求?

1 个答案:

答案 0 :(得分:2)

花了一段时间,但我设法使其适用于DotNetCore 3.1 +。

事实证明,我需要注入id002服务,因此,我决定不使用内联策略功能实现并添加所有内容。 首先是IHttpContextAccessor

IAuthorizationRequirement

不要离开一个空的类,我决定将要查找的声明名称传递给它,这样它就更通用了。

然后public class SubscriptionRequirement : IAuthorizationRequirement { public string ClaimName { get; } public SubscriptionRequirement(string claimName) { ClaimName = claimName; } } 可以访问声明(通过cookie或其他方式)和http请求。

AuthorizationHandler

要添加此政策:

public class SubscriptionRequirementHandler
    : AuthorizationHandler<SubscriptionRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public SubscriptionRequirementHandler(
        IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        SubscriptionRequirement subscriptionRequirement)
    {
        var claims = context.User.Claims;
        var subscriptionId = 
            context
                .User
                .FindFirst(x => x.Type == subscriptionRequirement.ClaimName)
                .Value;

        var httpContext = _httpContextAccessor.HttpContext;
        var subscriptionIdInUrl = httpContext.Request.RouteValues["subscriptionId"];
        if (subscriptionIdInUrl != null)
        {
            var isAuthorized = subscriptionId == subscriptionIdInUrl.ToString();
            if (isAuthorized)
            {
                context.Succeed(subscriptionRequirement);
                return Task.CompletedTask;
            }
        }

        context.Fail();
        return Task.CompletedTask;
    }
}

我还可以(对于Razor Pages来说是可选的)指定我想将此策略应用于特定文件夹中的所有页面

services
    .AddAuthorization(
        options =>
        {
            options.AddPolicy(
                "SameSubscriptionIdPolicy",
                policy =>
                    policy.Requirements.Add(new SubscriptionRequirement(ClaimTypes.Name)));
        });

最后,非常重要的是,注册处理程序和http

services
    .AddRazorPages(
        options =>
        {
            options.Conventions.AuthorizeFolder("/admin", "SameSubscriptionIdPolicy");
            options.Conventions.Add(new PageRouteTransformerConvention(new LowerCaseRoutes()));
        });

更新2020/06/18: 根据Kirk的评论,注册services.AddSingleton<IAuthorizationHandler, SubscriptionRequirementHandler>(); services.AddTransient<IHttpContextAccessor, HttpContextAccessor>(); 的一种更好的方法是使用已经可用的扩展名作为单例注册。

HttpContextAccessor
相关问题