我无法弄清楚这样做的正确和最有效的方法。我觉得日期和版本对于未来事情发生变化很重要(在我寻找答案时似乎发生了很多事情)。
使用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 Users
或Edit Users
,甚至是Delete Users
。
目标
根据给定的IdentityUser
和Claim
名称,我想知道用户是否拥有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版本。
答案 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);
}
}