ASP.NET Core Web API角色权限与菜单接口开发技术咨询
菜单接口与业务模块控制器开发方案
一、菜单接口开发(角色级菜单权限)
核心思路
基于现有Role-Permission关联关系,将菜单与权限绑定,通过用户所属角色过滤出可见菜单,最终返回树形结构供前端渲染。
1. 定义菜单与权限的关联规则
- 在
Permission表中新增PermissionType字段(枚举:Menu/Api),标记哪些权限对应菜单; - 每个菜单对应唯一的
PermissionKey(如menu_center、menu_calendar),在RolePermission中为不同角色分配对应菜单权限。
2. 菜单数据模型(DTO)
public class MenuDto { public int Id { get; set; } public string Name { get; set; } public string Path { get; set; } public int? ParentId { get; set; } public string Icon { get; set; } public List<MenuDto> Children { get; set; } = new List<MenuDto>(); }
3. 接口实现步骤
- 从JWT Token解析当前用户ID,通过
UserRole表获取用户所有角色ID; - 关联
Role、RolePermission、Permission表,筛选出PermissionType=Menu的权限记录; - 将权限映射为菜单数据,组装成树形结构返回。
示例控制器代码:
[ApiController] [Route("api/[controller]")] [Authorize] public class MenuController : ControllerBase { private readonly AppDbContext _dbContext; public MenuController(AppDbContext dbContext) { _dbContext = dbContext; } [HttpGet("user-menus")] public async Task<IActionResult> GetUserMenus() { // 从Claims获取用户ID var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (string.IsNullOrEmpty(userId)) return Unauthorized(); // 获取用户所有角色对应的菜单权限 var menuPermissions = await _dbContext.UserRoles .Where(ur => ur.UserId == int.Parse(userId)) .Join(_dbContext.RolePermissions, ur => ur.RoleId, rp => rp.RoleId, (ur, rp) => rp.PermissionId) .Join(_dbContext.Permissions, pid => pid, p => p.Id, (pid, p) => p) .Where(p => p.PermissionType == PermissionType.Menu) .ToListAsync(); // 映射为菜单DTO并组装树形结构 var menus = MapToMenuDtos(menuPermissions); var treeMenus = BuildMenuTree(menus); return Ok(treeMenus); } // 辅助方法:权限转菜单DTO private List<MenuDto> MapToMenuDtos(List<Permission> permissions) { return permissions.Select(p => new MenuDto { Id = p.Id, Name = p.Name, Path = p.Key, ParentId = p.ParentId, Icon = p.Icon }).ToList(); } // 辅助方法:构建树形菜单 private List<MenuDto> BuildMenuTree(List<MenuDto> menus) { var rootMenus = menus.Where(m => m.ParentId == null).ToList(); foreach (var root in rootMenus) { root.Children = menus.Where(m => m.ParentId == root.Id).ToList(); BuildChildTree(root, menus); } return rootMenus; } private void BuildChildTree(MenuDto parent, List<MenuDto> allMenus) { foreach (var child in parent.Children) { child.Children = allMenus.Where(m => m.ParentId == child.Id).ToList(); BuildChildTree(child, allMenus); } } }
4. 权限验证增强
- 接口添加
[Authorize]特性,限制仅已登录用户访问; - 可自定义
PermissionAuthorizeAttribute,进一步验证用户是否拥有对应菜单权限。
二、业务模块控制器(Center/Clinic/Tariff/Calendar)开发方案
1. 统一架构规范
每个业务模块独立创建控制器、服务层、DTO,遵循分层架构:
- 控制器:处理HTTP请求/响应,调用服务层逻辑;
- 服务层:封装业务逻辑,与数据访问层交互;
- DTO:定义输入(
CreateXXXDto)、输出(XXXDto)模型,避免直接暴露数据库实体。
2. 权限控制落地
- 为每个业务接口定义专属权限(如
center_read、clinic_create),在Permission表中添加记录; - 通过
RolePermission为不同角色分配权限(如医生可访问calendar_read,秘书可访问clinic_read); - 在控制器方法上添加授权特性,示例:
[ApiController] [Route("api/[controller]")] [Authorize] public class ClinicController : ControllerBase { private readonly IClinicService _clinicService; public ClinicController(IClinicService clinicService) { _clinicService = clinicService; } // 仅拥有clinic_read权限的角色可访问 [HttpGet] [Authorize(Policy = "clinic_read")] public async Task<IActionResult> GetClinics() { var clinics = await _clinicService.GetAllClinicsAsync(); return Ok(clinics); } // 仅拥有clinic_create权限的角色可访问 [HttpPost] [Authorize(Policy = "clinic_create")] public async Task<IActionResult> CreateClinic([FromBody] CreateClinicDto dto) { if (!ModelState.IsValid) return BadRequest(ModelState); var clinicId = await _clinicService.CreateClinicAsync(dto); return CreatedAtAction(nameof(GetClinicById), new { id = clinicId }, null); } [HttpGet("{id}")] [Authorize(Policy = "clinic_read")] public async Task<IActionResult> GetClinicById(int id) { var clinic = await _clinicService.GetClinicByIdAsync(id); if (clinic == null) return NotFound(); return Ok(clinic); } }
3. 授权策略配置
在Program.cs中配置基于权限的授权策略:
builder.Services.AddAuthorization(options => { var permissions = builder.Configuration.GetSection("Permissions").Get<List<string>>(); foreach (var perm in permissions) { options.AddPolicy(perm, policy => policy.RequireClaim("Permission", perm)); } });
注:登录生成JWT Token时,需将用户拥有的所有
PermissionKey添加到Claims中,格式为new Claim("Permission", permissionKey)。
4. 核心开发要点
- 数据访问:用EF Core实现CRUD,添加事务处理保证数据一致性;
- 异常处理:全局异常过滤器捕获异常,返回统一格式的错误响应;
- 复用性:通用CRUD逻辑封装为
BaseService/BaseController,减少重复代码; - 验证:用数据注解或FluentValidation做输入参数验证。
内容的提问来源于stack exchange,提问作者Viki
相关产品推荐
相关产品推荐

