【问题标题】:ASP .NET MVC Identity users multiple Groups , multiple rolesASP .NET MVC Identity 用户多个组,多个角色
【发布时间】:2021-09-08 12:05:03
【问题描述】:

我希望创建一个 ASP.NET 6 Core Identity 解决方案,用户可以在其中创建组。 创建者成为该组的管理员,并且可以将其他用户添加到该组 - (其他用户可能会成为该组的管理员)。 创建者还需要存在于具有和不具有管理员的其他组中。 是否有一种简单的方法可以在身份中使用角色作为具有策略/声明的组,或者是否需要一些新表?像这样 https://www.codeproject.com/Tips/5276188/Implementing-User-Groups-using-Claims-in-ASP-NET-I?fid=1962554&df=90&mpp=25&sort=Position&view=Normal&spc=Relaxed&prof=True

【问题讨论】:

  • 我认为您可以先阅读本文以获得更多见解
    johnatten.com/2014/04/20/…
  • 您需要为创建组的用户添加声明,例如GroupOwner,为他们创建的每个组。然后在您的组 CRUD 端点中,检查会话用户是否对该特定组具有该声明,或者返回 Forbid 响应。
  • 嗨,Yat,johnatten.com 链接是关于让超级用户/用户/管理员组授权每个组.. 不一样谢谢。我发布的文章提取了应用程序中的所有操作,并授权组中的用户执行操作。也不理想

标签: asp.net-mvc asp.net-core asp.net-identity


【解决方案1】:

单独使用声明就足以进行授权(例如,此用户是管理员吗,他能否将其他用户提升为管理员身份)。但是使用声明将您限制为字符串类型,它可以采用任意值。这给您带来了确保每个值都符合预期的负担。

要拥有更完善的数据库架构,您应该将成员资格存储在单独的表中。这意味着在用户和组之间创建多对多关系以存储成员资格,这将需要一个查找表,但是查看您如何使用 ASP.NET Core 5 和可能的 EF Core 5,它会为您创建此表.

但您可能需要存储用户何时以及由谁添加到组中。这意味着在查找表上存储一些数据。因此,如果您需要这些,您也应该为该查找表创建一个实体。

至于代码,这里是你的起点:

public record Group(string Name)
{
    public const string AdminClaimName = "group_admin";
    public Guid Id { get; init; } = Guid.Empty;
    public List<IdentityUser> Members { get; set; } = new List<IdentityUser>();
}

[ApiController]
public class UserGroupController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly ApplicationDbContext _db;

    public UserGroupController(UserManager<IdentityUser> userManager, ApplicationDbContext db)
    {
        _userManager = userManager;
        _db = db;
    }

    public record GroupCreateRequest(string GroupName);

    [HttpPost]
    public async Task<IActionResult> CreateGroup(GroupCreateRequest request,
                                                 CancellationToken cancellationToken = default)
    {
        var user = await _userManager.GetUserAsync(User);
        
        var group = new Group(request.GroupName);
        await _db.Set<Group>().AddAsync(group, cancellationToken);
        await _db.SaveChangesAsync(cancellationToken);
        await _userManager.AddClaimAsync(user, new Claim(type: Group.AdminClaimName, value: group.Id.ToString()));

        return Ok(group);
    }

    public record GroupMembershipRequest(string MemberUserId);

    [HttpPost("{id:guid}/membership")]
    public async Task<IActionResult> AddUserToGroup(Guid groupId,
                                     GroupMembershipRequest request,
                                     CancellationToken cancellationToken = default)
    {
        var user = await _userManager.GetUserAsync(User);
        var claims = await _userManager.GetClaimsAsync(user);

        // check if the current user is the admin of that group
        if (claims.Any(c => c.Type == Group.AdminClaimName && c.Value == groupId.ToString()))
        {
            return Forbid();
        }

        var member = await _userManager.FindByIdAsync(request.MemberUserId);
        var group = await _db.Set<Group>().FindAsync(groupId);
        group.Members.Add(member);
        await _db.SaveChangesAsync(cancellationToken);

        return NoContent();
    }


    public record UserPromotionRequest(Guid GroupId, string MemberUserId);

    [HttpPost("promote")]
    public async Task<IActionResult> PromoteUserToAdmin(UserPromotionRequest request,
                                                        CancellationToken cancellationToken = default)
    {
        var user = await _userManager.GetUserAsync(User);
        var claims = await _userManager.GetClaimsAsync(user);

        // check if the current user is the admin of that group
        if (claims.Any(c => c.Type == Group.AdminClaimName && c.Value == request.GroupId.ToString()))
        {
            return Forbid();
        }

        var member = await _userManager.FindByIdAsync(request.MemberUserId);
        var group = await _db.Set<Group>().FindAsync(request.GroupId);
        await _userManager.AddClaimAsync(member, new Claim(type: Group.AdminClaimName, value: group.Id.ToString()));
        return NoContent();
    }
}

(我省略了一些验证和空值检查,您需要自己添加。)

注意控制器的User 属性(当前登录的用户)和从数据库中获取的user 实例之间的区别。

【讨论】:

  • 为什么要使用自定义 Group 类,而不是 MS Identity Role 功能,它本质上与 Group 相同,并且与 User 具有多对多关系?
  • 这也可以。但对我来说,Role 并不意味着一个团体或一个团队。它感觉与授权上下文有关。成员资格和组不需要授权即可运行。这就是我将它们分开的原因。
  • 我最终将此作为一个简单的解决方案 github.com/rogwool/two> 使用身份登录效果很好 - 使用 AspNetUserRoles 中的 GroupId 键可能会更好。
猜你喜欢
  • 1970-01-01
  • 2011-07-05
  • 2019-05-01
  • 2018-09-20
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 2012-09-15
相关资源
最近更新 更多