如何用ASP.NET Identity适配可自定义权限组的现有数据库模型?
Hey there! Let's work through how to adapt your custom permission model to ASP.NET Identity while sticking to your requirements and best practices.
First, let's clarify a quick point about Claims: while MSDN notes Claims are about identifying a principal, they absolutely can represent permissions too—permissions are just a type of attribute that describes what the principal is allowed to do. So we can use Claims here, just in a structured way that aligns with your fixed permission enum.
We'll build a system that:
- Uses a fixed enum for your
AccessItem(to enforce only trusted permission IDs) - Lets admins create custom
AccessGroupcollections of these permissions - Aggregates a user's permissions from their assigned
AccessGroups at login - Integrates with ASP.NET Core's authorization system for declarative checks
1. Define Your Permission Enum
First, lock down your trusted AccessItem values as a strong-type enum. This ensures no arbitrary permissions can be added:
public enum Permission { CanDeleteSale, CanCreateCustomer, CanViewReports, // Add all your fixed permission IDs here }
2. Extend Identity Entities for Access Groups
Create the entities to map your User-AccessGroup-Permission relationships. We'll use many-to-many relationships to keep things flexible:
// AccessGroup: Admin-defined collection of permissions public class AccessGroup { public int Id { get; set; } public string Name { get; set; } // e.g., "Sales Managers", "Customer Support" // Link to permissions via join table public ICollection<AccessGroupPermission> AccessGroupPermissions { get; set; } = new List<AccessGroupPermission>(); // Link to users via join table public ICollection<UserAccessGroup> UserAccessGroups { get; set; } = new List<UserAccessGroup>(); } // Join table: AccessGroup <-> Permission public class AccessGroupPermission { public int AccessGroupId { get; set; } public AccessGroup AccessGroup { get; set; } public Permission Permission { get; set; } } // Join table: User <-> AccessGroup public class UserAccessGroup { public string UserId { get; set; } public ApplicationUser User { get; set; } public int AccessGroupId { get; set; } public AccessGroup AccessGroup { get; set; } }
Don't forget to add these DbSets to your ApplicationDbContext and configure the relationships in OnModelCreating.
3. Auto-Load Permissions into Claims at Login
We'll use a custom UserClaimsPrincipalFactory to automatically fetch a user's permissions from their AccessGroups and add them to their ClaimsIdentity when they log in. This way, permissions are always available for authorization checks:
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser> { private readonly ApplicationDbContext _dbContext; public CustomUserClaimsPrincipalFactory( UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor, ApplicationDbContext dbContext) : base(userManager, optionsAccessor) { _dbContext = dbContext; } public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user) { // Start with the default claims (like email, name) var principal = await base.CreateAsync(user); // Fetch all unique permissions from the user's AccessGroups var userPermissions = await _dbContext.UserAccessGroups .Where(uag => uag.UserId == user.Id) .SelectMany(uag => uag.AccessGroup.AccessGroupPermissions) .Select(agp => agp.Permission) .Distinct() .ToListAsync(); // Add each permission as a Claim with a consistent type var identity = (ClaimsIdentity)principal.Identity; foreach (var permission in userPermissions) { identity.AddClaim(new Claim("Permission", permission.ToString())); } return principal; } }
Register this factory in your Program.cs/Startup.cs:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();
4. Set Up Declarative Authorization Policies
Next, we'll create authorization policies for each permission in your enum. This lets you use the [Authorize] attribute with policy names tied directly to your enum values:
services.AddAuthorization(options => { // Auto-create a policy for every permission in the enum foreach (Permission permission in Enum.GetValues(typeof(Permission))) { var permissionName = permission.ToString(); options.AddPolicy(permissionName, policy => policy.RequireClaim("Permission", permissionName)); } });
5. Use Declarative Authorization in Your App
Now you can secure controllers or actions with your permission policies, just like you would with roles:
[Authorize(Policy = nameof(Permission.CanDeleteSale))] public IActionResult DeleteSale(int saleId) { // Your delete logic here return RedirectToAction(nameof(Index)); }
- Admin Flexibility: Admins can create any combination of
AccessGroups using your fixed permission set - Type Safety: The enum ensures only trusted permissions are used, no arbitrary strings
- Seamless Identity Integration: Uses ASP.NET Identity's built-in Claims system, so you don't have to reinvent the wheel for authentication/authorization
- Declarative Checks: Keeps your authorization logic clean and visible right in your controllers/actions
内容的提问来源于stack exchange,提问作者Vinicius Gonçalves

