我希望创建一个 ASP.NET Core 5 Identity 解决方案,用户可以在其中创建组。 创建者成为该组的管理员,并且可以将其他用户 Crud 到该组 - (然后其他用户可能成为该组的管理员)。 创建者还需要存在于其他组中,无论是否有管理员。 是否有一种简单的方法可以在 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
答案 0 :(得分:0)
使用声明本身就足以进行授权(例如,此用户是管理员,他能否将其他用户提升为管理员状态)。但是使用声明将您限制为字符串类型,它可以采用任意值。这给您带来了确保每个值都符合预期的负担。
要拥有更健全的数据库架构,您应该将成员资格存储在单独的表中。这意味着在用户和组之间创建多对多关系来存储成员资格,这将需要一个查找表,但是看到您如何使用 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
实例之间的区别。