.NET与Angular动态角色及权限管理实现方案咨询
.NET Web API动态角色权限迁移解决方案
问题背景
作为.NET和Angular新手,尝试将基于MVC视图的动态角色权限逻辑迁移到Web API时遇到实现失败问题,以下是针对代码的修正方案及完整实现思路。
核心问题分析
- Web API无需MVC的UI组件,原
AddIdentity中的AddDefaultUI属于冗余配置 - MVC依赖Cookie认证,Web API更适合无状态的JWT认证,适配Angular前端交互
- 原代码缺少权限核心类(
Permissions、PermissionPolicyProvider等)的实现 - API控制器参数绑定、异常处理不完善
分步解决方案
1. 修正认证授权配置(Startup.cs)
移除MVC UI相关配置,添加JWT认证支持:
namespace roles_permissions_mangement { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { // 注册权限核心服务 services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>(); services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>(); // 数据库上下文 services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // 配置Identity,移除UI组件 services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // 添加JWT认证 services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; }); services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "roles_permissions_mangement", Version = "v1" }); // 添加JWT认证支持到Swagger c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Description = "请输入JWT Token,格式为Bearer {token}", Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Bearer" }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } }); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "roles_permissions_mangement v1")); } app.UseHttpsRedirection(); app.UseRouting(); // 注意顺序:先认证后授权 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
2. 实现权限核心类
权限定义枚举类
namespace roles_permissions_mangement { public static class Permissions { public static class Products { public const string View = "Permissions.Products.View"; public const string Create = "Permissions.Products.Create"; public const string Edit = "Permissions.Products.Edit"; public const string Delete = "Permissions.Products.Delete"; } // 可添加其他模块权限,如Users、Orders等 public static List<string> GetAllPermissions() { var permissions = new List<string>(); // 反射获取所有权限常量 foreach (var prop in typeof(Permissions).GetNestedTypes().SelectMany(t => t.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))) { if (prop.FieldType == typeof(string)) { var value = prop.GetValue(null) as string; if (value != null) permissions.Add(value); } } return permissions; } } }
权限策略提供器
namespace roles_permissions_mangement { public class PermissionPolicyProvider : IAuthorizationPolicyProvider { public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; } public PermissionPolicyProvider(IOptions<AuthorizationOptions> options) { FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options); } public Task<AuthorizationPolicy> GetDefaultPolicyAsync() { return FallbackPolicyProvider.GetDefaultPolicyAsync(); } public Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { if (policyName.StartsWith("Permissions.", StringComparison.OrdinalIgnoreCase)) { var policy = new AuthorizationPolicyBuilder(); policy.AddRequirements(new PermissionRequirement(policyName)); return Task.FromResult(policy.Build()); } return FallbackPolicyProvider.GetPolicyAsync(policyName); } public Task<AuthorizationPolicy> GetFallbackPolicyAsync() { return FallbackPolicyProvider.GetFallbackPolicyAsync(); } } }
权限授权处理器
namespace roles_permissions_mangement { public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.Role || c.Type == "Permission")) return Task.CompletedTask; var permissions = context.User.Claims.Where(c => c.Type == "Permission").Select(c => c.Value); if (permissions.Contains(requirement.Permission)) context.Succeed(requirement); return Task.CompletedTask; } } public class PermissionRequirement : IAuthorizationRequirement { public string Permission { get; } public PermissionRequirement(string permission) { Permission = permission; } } }
RoleManager扩展方法
namespace roles_permissions_mangement.Extensions { public static class RoleManagerExtensions { public static async Task AddPermissionClaim(this RoleManager<IdentityRole> roleManager, IdentityRole role, string permission) { var existingClaims = await roleManager.GetClaimsAsync(role); if (!existingClaims.Any(c => c.Type == "Permission" && c.Value == permission)) { await roleManager.AddClaimAsync(role, new Claim("Permission", permission)); } } } }
3. 修正API控制器
RolesController(改用DTO,完善异常处理)
namespace roles_permissions_mangement.Controllers { [Route("api/[controller]")] [ApiController] [Authorize(Roles = "SuperAdmin")] public class RolesController : ControllerBase { private readonly RoleManager<IdentityRole> _roleManager; public RolesController(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; } [HttpGet] public async Task<ActionResult<IEnumerable<IdentityRole>>> GetRoles() { var roles = await _roleManager.Roles.ToListAsync(); return Ok(roles); } [HttpPost] public async Task<ActionResult> AddRole([FromBody] RoleCreateDto dto) { if (string.IsNullOrWhiteSpace(dto.RoleName)) return BadRequest(new { Message = "角色名称不能为空" }); var roleExists = await _roleManager.RoleExistsAsync(dto.RoleName); if (roleExists) return Conflict(new { Message = "该角色已存在" }); var result = await _roleManager.CreateAsync(new IdentityRole(dto.RoleName)); if (result.Succeeded) return Ok(new { Message = "角色添加成功" }); return BadRequest(new { Message = "角色创建失败", Errors = result.Errors.Select(e => e.Description) }); } } public class RoleCreateDto { public string RoleName { get; set; } } }
PermissionController(完善空值判断)
namespace roles_permissions_mangement.Controllers { [Route("api/[controller]")] [ApiController] [Authorize(Roles = "SuperAdmin")] public class PermissionController : ControllerBase { private readonly RoleManager<IdentityRole> _roleManager; public PermissionController(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; } [HttpGet("{roleId}")] public async Task<ActionResult<PermissionViewModel>> GetRolePermissions(string roleId) { var role = await _roleManager.FindByIdAsync(roleId); if (role == null) return NotFound(new { Message = "角色不存在" }); var model = new PermissionViewModel { RoleId = roleId }; var allPermissions = Permissions.GetAllPermissions() .Select(p => new RoleClaimsViewModel { Value = p, Selected = false }) .ToList(); var roleClaims = await _roleManager.GetClaimsAsync(role); var rolePermissionValues = roleClaims.Where(c => c.Type == "Permission").Select(c => c.Value); foreach (var permission in allPermissions) { permission.Selected = rolePermissionValues.Contains(permission.Value); } model.RoleClaims = allPermissions; return Ok(model); } [HttpPost] public async Task<ActionResult> UpdateRolePermissions([FromBody] PermissionViewModel model) { if (model == null || string.IsNullOrWhiteSpace(model.RoleId)) return BadRequest(new { Message = "请求参数无效" }); var role = await _roleManager.FindByIdAsync(model.RoleId); if (role == null) return NotFound(new { Message = "角色不存在" }); // 移除现有权限声明 var existingClaims = await _roleManager.GetClaimsAsync(role); var permissionClaims = existingClaims.Where(c => c.Type == "Permission"); foreach (var claim in permissionClaims) { await _roleManager.RemoveClaimAsync(role, claim); } // 添加选中的权限 var selectedPermissions = model.RoleClaims.Where(a => a.Selected).Select(a => a.Value); foreach (var permission in selectedPermissions) { await _roleManager.AddPermissionClaim(role, permission); } return Ok(new { Message = "角色权限更新成功" }); } } public class PermissionViewModel { public string RoleId { get; set; } public List<RoleClaimsViewModel> RoleClaims { get; set; } } public class RoleClaimsViewModel { public string Value { get; set; } public bool Selected { get; set; } } }
4. 配置JWT参数(appsettings.json)
{ "Jwt": { "Key": "Your_Super_Secret_Key_Here_Must_Be_At_Least_16_Characters", "Issuer": "https://localhost:5001", "Audience": "https://localhost:5001" }, "ConnectionStrings": { "DefaultConnection": "Your_SQL_Server_Connection_String" } }
替代实现思路
如果觉得自定义权限策略过于复杂,可直接使用[Authorize(Policy = "Permissions.Products.View")]标注API接口,结合JWT Token中携带的权限声明完成验证,无需自定义PolicyProvider。
内容的提问来源于stack exchange,提问作者Acil fr
相关产品推荐
相关产品推荐

