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

时间:2018-02-12 03:20:48

标签: c# asp.net asp.net-mvc linq asp.net-identity

我无法弄清楚这样做的正确和最有效的方法。我觉得日期和版本对于未来事情发生变化很重要(在我寻找答案时似乎发生了很多事情)。

使用Visual Studio 2017 v15.5.5,我开始使用个人帐户创建一个新的MVC ASP.NET Web应用程序(.Net Framework,而不是CORE)。我升级了所有的包。目前正在使用EntityFramework v6.2.0和Identity v2.2.1。

我做了什么

我手动实现了自己的RoleClaims版本。我有一个class定义了RoleClaims,另一个class定义了所有应用程序的Claims。在应用程序中,Claims用于定义可以执行的操作。例如,Claim可以是View UsersEdit 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中执行所有这些逻辑,基于以下内容,因此我很可能会做其他错误的事情。 =)随意发表评论。

ClaimsAuthorizationAttribute

[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 Claim是否有Roles。{/ p>

解决方案

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

1 个答案:

答案 0 :(得分:2)

假设您拥有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或添加新的{{}}}时缓存无效,这样您就无法使用&#39;陈旧&#39;数据

注意上面的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);
    }
}