使用OWIN Identity v2声明来跟踪WebAPI2应用程序中的自定义属性

nki*_*kes 4 c# membership claims-based-identity asp.net-identity asp.net-web-api2

让我的脑袋围绕新的身份框架,并试图找出如何最好地处理自定义用户属性.我已经尝试扩展IdentityUser,它可以存储信息,但到目前为止需要额外的db调用来恢复属性.我期待切换到使用声明来存储/检索此信息.

首先,我想要存储/检索的特定道具对于单个用户(多对一)不是唯一的.考虑将用户组合在一个自定义组结构中.我想存储GroupId以用于其他相关实体.

我能够存储GroupId(目前使用ClaimTypes.NameIdentifier,我不认为它是该类型的正确用法,但是......).但是,当我去检索该值时,声明集合中找不到声明类型.它在数据库中,所以我知道它就在那里.我错过了什么.

FWIW:由于它是WebAPI,我没有使用传统的登录.我正在使用令牌身份验证.

当我创建用户时,我有类似的东西:

public async Task<IdentityResult> CreateUserAsync(string email, string password, string groupId)
    {
        var userId = ObjectId.GenerateNewId(DateTime.UtcNow).ToString(); // yes, it's a NoSQL store
        var user = new ApplicationUser
        {
            Id = userId,
            UserName = email
        };

        var claim = new IdentityUserClaim { ClaimType = ClaimTypes.NameIdentifier, ClaimValue = groupId, UserId = userId, Id = ObjectId.GenerateNewId(DateTime.UtcNow).ToString() };
        user.Claims.Add(claim);

        var result = await _UserManager.CreateAsync(user, password);
        return result;
    }
Run Code Online (Sandbox Code Playgroud)

这会创建一个看起来合适的数据库条目.

当我检索该值时,我得到空引用错误.这是通过扩展方法的代码:

public static string GetGroupId(this IIdentity identity)
    {
        var claimsIdentity = identity as ClaimsIdentity;
        return claimsIdentity == null ? "" : claimsIdentity.FindFirst(ClaimTypes.NameIdentifier).Value;
    }
Run Code Online (Sandbox Code Playgroud)

尝试获取Value时错误命中,因为FindFirst返回空值.

这里的任何提示或更好/最佳实践将不胜感激!老实说,我更愿意将它存储在ApplicationUser : IdentityUser对象上,但我找不到一种简单的方法来检索我的api控制器上下文中的User.Identity,而无需额外调用db.

tra*_*max 5

您对将额外数据存储为声明的直觉感觉是正确的,但实现有点破碎.

我建议您为自己的域信息创建自己的声明类型.不要重用框架提供的声明类型.原因是ClaimTypes.NameIdentifierUser.Id.该框架本身为所有用户添加了标准的声明列表:

  • User.Id =>表示为 ClaimTypes.NameIdentifier
  • Username =>表示为'ClaimTypes.Name'
  • ProviderName =>表示为ClaimTypes.ProviderName(不是100%肯定这个); 通常值是"ASP.NET身份"
  • SecurityStamp值(不确定它的声明类型名称)
  • 分配给用户的所有角色都存储为 ClaimTypes.Role

所以在你的情况下,你试图覆盖声明的价值 User.Id是非常重要的,我认为 - )

现在,让我们尝试解决您的编码问题.创建用户时,在创建用户对象后添加声明:

public async Task<IdentityResult> CreateUserAsync(string email, string password, string groupId)
{
    var user = new ApplicationUser
        {
            Id = userId,
            UserName = email
        };

    var userCreateResult = await _UserManager.CreateAsync(user, password);
    if(!userCreateResult.IsSuccess)
    {
        // user creation have failed - need to stop the transaction
        return userCreateResult;
    }

    // better to have a class with constants representing your claim types
    var groupIdClaim = new Claim("MyApplication:GroupClaim", ObjectId.GenerateNewId(DateTime.UtcNow).ToString());

    // this will save the claim into the database. Next time user logs in, it will be added to Principal.Identity
    var claimAddingResult = await _UserManager.AddClaimAsync(userId, groupIdClaim);
    return claimAddingResult;
}
Run Code Online (Sandbox Code Playgroud)

至于扩展方法,我通常使用IPrincipalClaimsPrincipal.但IIdentity也是可行的.不要忘记你可以ClaimsPrincipal通过电话访问任何地方ClaimsPrincipal.Current.

这就是我通常使用扩展方法的方法:

public static string GetGroupId(this ClaimsPrincipal principal)
{
    var groupIdClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApplication:GroupClaim");
    if (personIdClaim != null)
    {
        return groupIdClaim.Value;
    }
    return String.Empty;
}
Run Code Online (Sandbox Code Playgroud)

因此,在您的方法中,您将检索groupId为当前登录用户分配的内容,如下所示:

var groupId = ClaimsPrincipal.Current.GetGroupId();
Run Code Online (Sandbox Code Playgroud)

希望这能澄清你的困惑!