简体   繁体   English

在 .NET 中使用自定义授权属性实现基于声明的动态身份验证 6

[英]Implementing dynamic claims-based auth with custom authorization attribute in .NET 6

I am creating a .NET 6 application that connects to an external identity provider for authentication using cookie authentication and OpenIdConnect.我正在创建一个 .NET 6 应用程序,它连接到外部身份提供者以使用 cookie 身份验证和 OpenIdConnect 进行身份验证。 The external provider returns the user's JWT. Within that JWT, there are claims which denote which resources the user should have access to.外部提供者返回用户的 JWT。在这个 JWT 中,有声明表明用户应该访问哪些资源。 These claims have a custom claim type of "privilege" and a unique value (something like "privilege": "create_blog").这些声明具有自定义声明类型“特权”和唯一值(类似于“特权”:“create_blog”)。

I see that you can add a policy to check for a specific claim, like:我看到您可以添加一项政策来检查特定声明,例如:

builder.Services.AddAuthorization(options =>
{
   options.AddPolicy("CreateBlog", policy => policy.RequireClaim("privilege", "create_blog"));
});

However, in our application, I want to be able to specify the claim value via the attribute (because our system has hundreds of different privileges) like:但是,在我们的应用程序中,我希望能够通过属性指定声明值(因为我们的系统有数百种不同的权限),例如:

[PrivilegeAuthorize("create_blog")]
public IActionResult CreateBlog { ... }

I tried creating a custom authorization provider & authorization handler as described in the docs .我尝试按照文档中的描述创建自定义授权提供程序和授权处理程序。 That gets triggered & executed fine.这被触发并执行得很好。 However, when the provider fails (because the user is unauthenticated or the claim is missing), it doesn't trigger the right response.但是,当提供者失败时(因为用户未经身份验证或声明丢失),它不会触发正确的响应。 The user is redirected to /Account/Login (the default LoginPath for the CookieAuthenticationOptions).用户被重定向到 /Account/Login(CookieAuthenticationOptions 的默认登录路径)。 But I want it to OIDC identity provider if the user is not authenticated, and show my "Access Denied" page if the claim is missing.但如果用户未通过身份验证,我希望将它提供给 OIDC 身份提供者,如果声明丢失,则显示我的“拒绝访问”页面。

I was finally able to develop a solution for this problem that conforms with Microsoft's suggested approaches for creating authentication policies and custom authorization providers.我终于能够为这个问题开发一个解决方案,该解决方案符合 Microsoft 建议的创建身份验证策略和自定义授权提供程序的方法。 I created a custom authorization policy provider which uses Microsoft's provided AuthorizationOptions.AddPolicy(string, Action<AuthorizationPolicyBuilder>) method instead of instantiating a new AuthorizationPolicyBuilder for building the policy.我创建了一个自定义授权策略提供程序,它使用 Microsoft 提供的AuthorizationOptions.AddPolicy(string, Action<AuthorizationPolicyBuilder>)方法,而不是实例化一个新的 AuthorizationPolicyBuilder 来构建策略。 I implemented that policy provider using a custom authorization attribute.我使用自定义授权属性实现了该策略提供者。

The content of the custom authorization provider is as follows:自定义授权提供者的内容如下:

public class PrivilegeAuthorizationProvider : IAuthorizationPolicyProvider
{
    private readonly AuthorizationOptions _options;
    private Task<AuthorizationPolicy>? _cachedDefaultPolicy;
    private Task<AuthorizationPolicy?>? _cachedFallbackPolicy;

    public PrivilegeAuthorizationProvider (IOptions<AuthorizationOptions> options)
    {
        this._options = options.Value;
    }

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        if (this._cachedDefaultPolicy == null || this._cachedDefaultPolicy.Result != this._options.DefaultPolicy)
        {
            this._cachedDefaultPolicy = Task.FromResult(this._options.DefaultPolicy);
        }

        return this._cachedDefaultPolicy;
    }

    public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
    {
        if (this._cachedFallbackPolicy == null || this._cachedFallbackPolicy.Result != this._options.FallbackPolicy)
        {
            this._cachedFallbackPolicy = Task.FromResult(this._options.FallbackPolicy);
        }

        return this._cachedFallbackPolicy;
    }

    public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
    {
        AuthorizationPolicy _policy = this._options.GetPolicy(policyName);
        if (_policy != null)
        {
            return Task.FromResult(_policy);
        }

        if (_policy == null && policyName.StartsWith(PrivilegeAuthorizeAttribute.PolicyPrefix, StringComparison.OrdinalIgnoreCase))
        {
            string[] _splitPolicyName = policyName.Split(PrivilegeAuthorizeAttribute.PolicyNameSeparator);

            if (_splitPolicyName.Length == 2)
            {
                string[] _values = _splitPolicyName[1].HasValue()
                    ? _splitPolicyName[1].Split(PrivilegeAuthorizeAttribute.ClaimValuesSeparator)
                    : Array.Empty<string>();

                if (_values.Length > 0)
                {
                    this._options.AddPolicy(policyName, policy => policy.RequireClaim("privilege", _values));
                    _policy = this._options.GetPolicy(policyName);
                }
            }
        }

        return Task.FromResult(_policy);
    }
}

In the application's program.cs (or startup.cs), the custom authorization provider is registered like:在应用程序的 program.cs(或 startup.cs)中,自定义授权提供程序的注册方式如下:

_builder.Services.AddSingleton<IAuthorizationPolicyProvider, PrivilegeAuthorizationProvider>();

_builder.Services.AddAuthorization();
_builder.Services.AddAuthentication(opt =>
    {
        opt.DefaultScheme = "Cookies";
        opt.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", opt => { ... });

The custom authorization attribute is as follows:自定义授权属性如下:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class PrivilegeAuthorizeAttribute : AuthorizeAttribute
{
    internal const char ClaimValuesSeparator = ",";
    internal const char PolicyNameSeparator = ":";
    internal const string PolicyPrefix = "PrivilegeAuthorize";

    public PrivilegeAuthorizeAttribute(string value)
    {
        this.Values = new[] { value };
    }

    public PrivilegeAuthorizeAttribute(params string[] values)
    {
        this.Values = values;
    }

    protected string?[] Values
    {
        get
        {
            if (this.Policy.HasValue())
            {
                string[] _splitPolicy = this.Policy!.Split(PolicyNameSeparator);
                if (_splitPolicy.Length == 2 && _splitPolicy[1].HasValue()
                {
                    return _splitPolicy[1].Split(ClaimValuesSeparator);
                }
            }

            return Array.Empty<string>();
        }
        set
        {
            this.Policy = $"{PolicyPrefix}{PolicyNameSeparator}{string.Join(ClaimValuesSeparator, value)}";
        }
    }
}

Then the attribute can be applied to a controller, an action method, or a razor page like:然后该属性可以应用于 controller、操作方法或 razor 页面,例如:

[PrivilegeAuthorize("MySpecialPrivilegeClaimValue")]
public class Index : PageModel
{
   public void OnGet()
   { }
}

With this, the application checks the user's token for a claim of type "privilege" and a value which is specified in the attribute parameter.有了这个,应用程序检查用户的令牌是否有类型为“特权”的声明和在属性参数中指定的值。 If the user isn't authenticated, they're redirected to the OIDC provider.如果用户未通过身份验证,他们将被重定向到 OIDC 提供商。 If the user is authenticated, but the claim doesn't exist, the user is redirected to the application's internal "Access Denied" page.如果用户已通过身份验证,但声明不存在,则用户将被重定向到应用程序的内部“拒绝访问”页面。 If the user is authenticated and has the proper claim, they can see the desired page.如果用户通过身份验证并拥有适当的声明,他们就可以看到所需的页面。

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

相关问题 如何使用基于声明的授权保护ASP.NET Core 2.1中的静态文件夹 - How to protect static folder in asp.net core 2.1 using claims-based authorization 基于声明的授权,我可以避免例外吗? - Claims-based authorization, can I avoid exceptions? Sitecore 9如何建立基于声明的Active Directory授权 - Sitecore 9 how to establish Claims-based Active Directory Authorization 视图和视图模型如何在MVC 5中利用基于声明的授权? - How can Views and ViewModels utilize claims-based authorization in MVC 5? ASP.NET MVC基于声明的用户使用“外部”身份验证 - ASP.NET MVC claims-based users with “external” authentication ASP.NET MVC绕过/模拟基于ws联盟/声明的身份验证 - ASP.NET MVC bypass/simulate ws-federation/claims-based authentication 如何在Asp.net MVC中使用Identity 2.0实现基于声明的身份验证 - How to implement claims-based authentication using Identity 2.0 in Asp.net MVC ASP.NET Core中基于声明的授权 - Claims based authorization in ASP.NET Core 为基于声明的安全性实现自定义属性 - Implementing custom atributes for claims based security 使用用户名/密码或客户端证书的基于声明的身份验证 - Claims-based Authentication with Username/Password OR Client Certificate
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM