You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET与Angular动态角色及权限管理实现方案咨询

.NET Web API动态角色权限迁移解决方案

问题背景

作为.NET和Angular新手,尝试将基于MVC视图的动态角色权限逻辑迁移到Web API时遇到实现失败问题,以下是针对代码的修正方案及完整实现思路。

核心问题分析

  1. Web API无需MVC的UI组件,原AddIdentity中的AddDefaultUI属于冗余配置
  2. MVC依赖Cookie认证,Web API更适合无状态的JWT认证,适配Angular前端交互
  3. 原代码缺少权限核心类(Permissions、PermissionPolicyProvider等)的实现
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:07:33