检查用户是否具有具有特定声明的角色



我无法弄清楚这样做的正确和最有效的方法。我觉得日期和版本对于将来发生变化很重要(似乎经常发生,因为我正在寻找答案)。

使用Visual Studio 2017v15.5.5,我使用个人帐户启动了一个新的MVC ASP.NET Web应用程序(.Net Framework,而不是CORE)。我升级了所有软件包。当前使用EntityFrameworkv6.2.0 和Identityv2.2.1。

我做了什么

我手动实现了我自己的RoleClaims版本。我有一个定义RoleClaimsclass和另一个定义所有应用程序Claimsclass。在应用程序中,Claims用于定义可以执行的操作。例如,Claim可以是View Users的,也可以是Edit Users的,甚至是Delete Users的。

目标

根据给定的IdentityUserClaim名称,我想知道用户是否有该Claim

public class RoleClaim {
public int Id { get; set; }
public string RoleId { get; set; }
public IdentityRole Role { get; set; }
public int ClaimId { get; set; }
public Claim Claim { get; set; }
}
public class Claim {
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ClaimType { get; set; }
public virtual ICollection<RoleClaim> RoleClaims { get; set; }
}

更多背景

我更喜欢在Linq表达式中给出答案,但Linq查询也可以。

此外,我正在自定义AuthorizeAttribute中执行所有这些逻辑,基于以下内容,因此我很有可能做错了其他事情。 =)随意评论它。

  • 编写自己的自定义 ASP.Net MVC [授权] 属性
  • System.Web.Http.AuthorizeAttribute.cs

声明授权属性

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ClaimsAuthorizationAttribute : AuthorizeAttribute {
private static readonly string[] _emptyArray = new string[0];
private string _claims;
private string[] _claimsSplit = _emptyArray;
public string Claims {
get => _claims ?? string.Empty;
set {
_claims = value;
_claimsSplit = SplitString(value);
}
}
public override void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null) {
throw new ArgumentException("filterContext");
}
if (AuthorizeCore(filterContext.HttpContext)) {
// allowed... research anything else needed to be done
} else {
HandleUnauthorizedRequest(filterContext);
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext) {
if (httpContext == null) {
throw new ArgumentException("httpContext");
}
var user = httpContext.User;
if (!user.Identity.IsAuthenticated) {
return false;
}
/*
* _claimsSplit contains all allowed Claims with access
* 
* Based on the list of Claims, check if any of the Roles 
* the user is a member of has at least the same Claim
* 
* OR
* 
* Based on the users' Roles, check if any of those 
* roles has at least one of the claims that were passed in
* 
* 
* 
* Should I check for any or should it be ALL Claims 
* passed in? or should I pass another variable (bool) 
* allowing the ability to decide if it should be 
* at least 1 or all?
*/
return true;
}
internal static string[] SplitString(string original) {
if (string.IsNullOrEmpty(original)) {
return _emptyArray;
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !string.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}

总结

我需要帮助找出一种有效的方法来检查User是否通过他们的RolesClaim

溶液

对于任何感兴趣的人,感谢Stephen Muecke,我能够创建解决方案。任何有兴趣的人都可以查看我的 ClaimsAuthorizationAttribute.cs 版本。

假设你有User(例如var User = db.Users.FirstOrDefault(u => u.UserName == user.Identity.Name),您可以先获取用户的角色 ID 集合

var roleIds = User.Roles.Select(x => x.Id);

然后获取具有其中一个角色 ID 的所有声明的名称

var claims = db.RoleClaims.Where(x => roleIds.Contains(x => x.RoleId)).Select(x => x.Claim.Name);

最后测试是否有任何匹配项

if (claims.Any(x => _claimsSplit.Contains(x)))

但是,由于这将在每个请求上调用,因此应考虑将结果缓存在MemoryCache中(假设RoleClaim不会经常更改)。一种方法是有一个Dictionary<string, IEnumerable<string>>,其中键是RoleId,值是Claim名称的集合。

您可以使用所有值填充启动时的Dictionary,也可以根据需要添加到其中。伪代码将是这样的

private const string key = "RoleClaims"
private bool HasClaim(string[] requiredClaims)
{
// Check the cache
Dictionary<string, IEnumerable<string>> roleClaims = Cache.Get(key)
if (roleClaims == null)
{
roleClaims = new Dictionary<string, IEnumerable<string>>();
Cache.Set(key, roleClaims, 240);
}
foreach (var role in roleIds)
{
IEnumerable<string> claims;
if (roleClaims.ContainsKey(role))
{
claims = roleClaims[role];
}
else
{
claims = db.RoleClaims.Where(roleIds == role).Select(x => x.Claim.Name);
roleClaims.Add(role, claims)
}
if (claims.Any(x => requiredClaims.Contains(x)))
{
return true // exit
}
}
return false;
}

并称其为AuthorizeCore()

return HasClaim(_claimsSplit);

您只需要确保在修改现有RoleClaim或添加新时缓存失效,这样您就不会使用"过时"数据。

请注意,上面的Cache类是

public static class Cache
{
public static object Get(string key)
{
return MemoryCache.Default[key];
}
public static void Set(string key, object data, int duration = 30)
{
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(duration);
MemoryCache.Default.Add(new CacheItem(key, data), policy);
}
public static void Invalidate(string key)
{
MemoryCache.Default.Remove(key);
}
}

相关内容

  • 没有找到相关文章

最新更新