[英]How to protect static folder in asp.net core 2.1 using claims-based authorization
[英]Implementing dynamic claims-based auth with custom authorization attribute in .NET 6
我正在創建一個 .NET 6 應用程序,它連接到外部身份提供者以使用 cookie 身份驗證和 OpenIdConnect 進行身份驗證。 外部提供者返回用戶的 JWT。在這個 JWT 中,有聲明表明用戶應該訪問哪些資源。 這些聲明具有自定義聲明類型“特權”和唯一值(類似於“特權”:“create_blog”)。
我看到您可以添加一項政策來檢查特定聲明,例如:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CreateBlog", policy => policy.RequireClaim("privilege", "create_blog"));
});
但是,在我們的應用程序中,我希望能夠通過屬性指定聲明值(因為我們的系統有數百種不同的權限),例如:
[PrivilegeAuthorize("create_blog")]
public IActionResult CreateBlog { ... }
我嘗試按照文檔中的描述創建自定義授權提供程序和授權處理程序。 這被觸發並執行得很好。 但是,當提供者失敗時(因為用戶未經身份驗證或聲明丟失),它不會觸發正確的響應。 用戶被重定向到 /Account/Login(CookieAuthenticationOptions 的默認登錄路徑)。 但如果用戶未通過身份驗證,我希望將它提供給 OIDC 身份提供者,如果聲明丟失,則顯示我的“拒絕訪問”頁面。
我終於能夠為這個問題開發一個解決方案,該解決方案符合 Microsoft 建議的創建身份驗證策略和自定義授權提供程序的方法。 我創建了一個自定義授權策略提供程序,它使用 Microsoft 提供的AuthorizationOptions.AddPolicy(string, Action<AuthorizationPolicyBuilder>)
方法,而不是實例化一個新的 AuthorizationPolicyBuilder 來構建策略。 我使用自定義授權屬性實現了該策略提供者。
自定義授權提供者的內容如下:
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);
}
}
在應用程序的 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 => { ... });
自定義授權屬性如下:
[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)}";
}
}
}
然后該屬性可以應用於 controller、操作方法或 razor 頁面,例如:
[PrivilegeAuthorize("MySpecialPrivilegeClaimValue")]
public class Index : PageModel
{
public void OnGet()
{ }
}
有了這個,應用程序檢查用戶的令牌是否有類型為“特權”的聲明和在屬性參數中指定的值。 如果用戶未通過身份驗證,他們將被重定向到 OIDC 提供商。 如果用戶已通過身份驗證,但聲明不存在,則用戶將被重定向到應用程序的內部“拒絕訪問”頁面。 如果用戶通過身份驗證並擁有適當的聲明,他們就可以看到所需的頁面。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.