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

如何扩展ASP.NET Core 2.0角色以实现多场所差异化权限管控?

实现ASP.NET Core 2.0多维度权限控制(基于场所+操作的细粒度授权)

针对你提出的多维度权限控制需求(结合归属场所、CRUD操作、跨场所授权),在ASP.NET Core 2.0中可以通过自定义授权策略+数据模型设计来实现,完全摆脱单一角色的限制。下面是一套完整的落地方案:

1. 核心数据模型设计

首先需要梳理清楚权限的关联关系:用户-场所-操作权限,所以需要设计以下几个核心模型:

// 场所实体:存储所有归属场所信息
public class Location
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    // 可添加层级字段(如ParentId)支持场所上下级关联
}

// 权限项实体:对应CRUD等操作权限
public class Permission
{
    public int Id { get; set; }
    public string Code { get; set; } // 唯一标识,如"Create", "Read", "Update", "Delete"
    public string DisplayName { get; set; } // 显示名称,如"创建权限"
}

// 用户-场所-权限关联表:核心关联实体,记录用户在指定场所拥有的具体权限
public class UserLocationPermission
{
    public int UserId { get; set; }
    public int LocationId { get; set; }
    public int PermissionId { get; set; }

    // 导航属性(EF Core关联用)
    public ApplicationUser User { get; set; }
    public Location Location { get; set; }
    public Permission Permission { get; set; }
}

同时,记得在ApplicationDbContext中配置这些实体的DbSet以及关联关系,比如UserLocationPermission的复合主键:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // 配置用户-场所-权限的复合主键
    modelBuilder.Entity<UserLocationPermission>()
        .HasKey(ulp => new { ulp.UserId, ulp.LocationId, ulp.PermissionId });
}

2. 自定义权限授权Handler

ASP.NET Core的默认角色授权无法满足这种多维度判断,所以需要实现自定义的IAuthorizationHandler来处理场所+操作的权限校验:

定义权限需求类

// 权限需求:传递需要校验的操作权限码和场所ID
public class LocationPermissionRequirement : IAuthorizationRequirement
{
    public string PermissionCode { get; }
    public int? TargetLocationId { get; } // 可为空,为空则校验用户所有有权限的场所

    public LocationPermissionRequirement(string permissionCode, int? targetLocationId = null)
    {
        PermissionCode = permissionCode ?? throw new ArgumentNullException(nameof(permissionCode));
        TargetLocationId = targetLocationId;
    }
}

实现授权Handler

public class LocationPermissionHandler : AuthorizationHandler<LocationPermissionRequirement>
{
    private readonly ApplicationDbContext _dbContext;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public LocationPermissionHandler(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor)
    {
        _dbContext = dbContext;
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LocationPermissionRequirement requirement)
    {
        // 获取当前用户ID
        var userIdClaim = context.User.FindFirst(ClaimTypes.NameIdentifier);
        if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out int userId))
        {
            context.Fail();
            return;
        }

        // 获取当前请求对应的场所ID(这里假设从路由参数获取,可根据业务调整为请求头/Session等)
        int currentLocationId;
        if (!int.TryParse(_httpContextAccessor.HttpContext.Request.RouteValues["locationId"]?.ToString(), out currentLocationId))
        {
            // 如果没有指定场所ID,可以判断用户是否有全局权限,或者直接拒绝
            context.Fail();
            return;
        }

        // 校验用户在当前场所是否拥有指定权限
        var hasPermission = await _dbContext.UserLocationPermissions
            .Include(ulp => ulp.Permission)
            .AnyAsync(ulp => ulp.UserId == userId 
                             && ulp.LocationId == currentLocationId 
                             && ulp.Permission.Code == requirement.PermissionCode);

        if (hasPermission)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }
    }
}

3. 注册授权服务

在Startup.cs的ConfigureServices方法中注册自定义授权策略和Handler:

public void ConfigureServices(IServiceCollection services)
{
    // ... 其他服务注册

    // 注册授权服务并定义权限策略
    services.AddAuthorization(options =>
    {
        options.AddPolicy("CreateAtLocation", policy =>
            policy.Requirements.Add(new LocationPermissionRequirement("Create")));
        options.AddPolicy("ReadAtLocation", policy =>
            policy.Requirements.Add(new LocationPermissionRequirement("Read")));
        options.AddPolicy("UpdateAtLocation", policy =>
            policy.Requirements.Add(new LocationPermissionRequirement("Update")));
        options.AddPolicy("DeleteAtLocation", policy =>
            policy.Requirements.Add(new LocationPermissionRequirement("Delete")));
    });

    // 注册自定义授权Handler
    services.AddScoped<IAuthorizationHandler, LocationPermissionHandler>();
    // 注册HttpContextAccessor(用于获取当前请求上下文)
    services.AddHttpContextAccessor();
}

4. 在控制器中使用权限校验

直接在Action或控制器上添加[Authorize]特性并指定对应的策略即可:

[Authorize(Policy = "ReadAtLocation")]
public async Task<IActionResult> GetLocationEntities(int locationId)
{
    // 业务逻辑:获取指定场所下的实体数据
    var entities = await _dbContext.Entities.Where(e => e.LocationId == locationId).ToListAsync();
    return Ok(entities);
}

[Authorize(Policy = "CreateAtLocation")]
[HttpPost]
public async Task<IActionResult> CreateEntity(int locationId, [FromBody] EntityDto dto)
{
    // 业务逻辑:在指定场所下创建实体
    var entity = new Entity
    {
        LocationId = locationId,
        Name = dto.Name,
        // 其他属性赋值
    };
    _dbContext.Entities.Add(entity);
    await _dbContext.SaveChangesAsync();
    return CreatedAtAction(nameof(GetLocationEntities), new { locationId, id = entity.Id }, entity);
}

5. 权限管理与默认权限配置

  • 默认权限分配:在用户创建时,根据其岗位职责分配默认归属场所,并自动添加该场所的默认权限(比如Read权限),可以在用户注册逻辑或后台创建用户时实现。
  • 跨场所授权:开发一个权限管理后台页面,允许管理员选择用户、场所、操作权限进行关联,直接操作UserLocationPermission表即可完成权限授予。
  • 性能优化:为了避免每次授权都查询数据库,可以在用户登录时将其所有权限(用户-场所-权限集合)缓存到内存或Redis中,授权Handler优先从缓存中校验权限。

内容的提问来源于stack exchange,提问作者Adam Wells

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:25:35