高级权限管理 - 依赖于角色和实体



这是一个相当高级的设计问题,我似乎找不到一个简单的答案。

它类似于这个问题: 使用实体框架管理实体访问和权限

超薄版本的系统:

  1. 我们有 6 种不同类型的用户,分为 3 个类别。

    • 供应商:用户
    • 供应商:管理员用户
    • 内部工厂:用户
    • 内部工厂:管理员用户
    • 管理员:维护用户
    • 管理员:超级用户 管理员、供应商和内部工厂组都是相互排斥的。
  2. 我们有一个页面,允许人们编辑实体。让我们以"货件"为例。货件仅由供应商或管理员创建。只有在以下情况下才能编辑货件。

    • 这是最后编辑的货件(适用于所有角色,管理员角色除外。
    • 它由与用户相同的供应商公司拥有(适用于供应商角色(
    • 它由与用户相关的供应商公司拥有 内部工厂.(仅适用于内部工厂角色(

所以问题是,编辑权限取决于对象的属性,以及用户角色。所以我的代码最终变得非常混乱,我经常犯错误 - 这是一些不同代码的摘录,所以你明白了。我没有实施我上面所说的。

权限被授予某些角色,这些角色可能以错误的方式使用,然后从实体属性确定其他内容以查看它是否是"我们的"货件等

public bool CheckCanEdit(EntityDto input)
{
if (
IsGranted(PermissionNames.EditAnyShipments)
||
(IsGranted(PermissionNames.EditShipments) && _shipmentsRepo.GetAllForEdit().Where(s =>
s.Id == input.Id && s.SupplierId == AbpSession.TenantId).Count() > 0
)
||
(IsGranted(PermissionNames.EditClientShipments) && _shipmentsRepo.GetAllForEdit().Where(s =>
s.Id == input.Id && s.FactoryId == AbpSession.TenantId).Count() > 0
)
)
{
return true;
}

return false;
}

这实际上是一个更简单的情况,但你可以看到它的发展方向。

我想要的是有一个可以调用的简单方法,它会告诉我用户是否可以编辑实体。通过将此方法与所有其他"更新"等功能一起放入服务"货件"中,我已经实现了"可以轻松调用"功能。这很棒,因为我也可以从 API 调用它,并且 UI 可以获得相同的逻辑。

但是,我觉得这是 1(沉重,2(凌乱。 它很重,因为例如,如果我想加载 100 个货件的列表,并显示"编辑"按钮,则需要在 UI 知道是否显示按钮之前单独调用它们。 它很混乱,因为"CheckCanEdit"函数中的代码不好听,也不合逻辑。我希望在设计方面有更好的东西;有关如何以"最佳实践"方式处理此问题的教程或讲座或解释。

谢谢。

这个问题的一个解决方案是使用规范模式,这是ASP ABP中所示的实现。

规范可以是:EditableShipments在此规范中,将根据用户具有的权限返回表达式。

public class EditableShipmentSpecification : Specification<Shipment>, ITransientDependency
{
private readonly IPermissionChecker _permiss;
public EditableShipmentSpecification(IPermissionChecker permis)
{
_permiss = permis;
}
public override Expression<Func<Shipment, bool>> ToExpression()
{
//if this is true, we can edit regardless
bool skipermission = _permiss.IsGranted(PermissionNames.AllowShipmentEditEvenIfInvoiced);
//Admins
if (_permiss.IsGranted(PermissionNames.FullEdit_Any_Shipment))
return (shipment) => (!shipment.IsDeleted);

//FactoryAdmins
// Only last is fully editable
if (_permiss.IsGranted(PermissionNames.CanEditLastShipment))
return (shipment) => !shipment.IsDeleted &&
shipment.Order.IsDeleted == false &&
shipment.Order.Shipment.Where(s => s.IsDeleted == false).Max(o => o.BatchNum) == shipment.BatchNum
&& (shipment.InvoiceId == null || skipermission)
;
//default
return (shipment) => (false);
}
}

这是我最终的解决方案,效果很好,因为我们可以使用SpecificationsToExpression函数将其用作查询,或者使用IsSatisfiedBy函数进行快速比较以启用视图上的Edit按钮等。

最新更新