使业务层的方法安全.最佳实践/最佳模式

gsh*_*arp 9 .net c# security design-patterns

我们正在使用ASP.NET进行大量的AJAX"页面方法"调用.页面中定义的Web服务调用BusinessLayer中的方法.为了防止黑客调用页面方法,我们希望在BusinessLayer中实现一些安全性.

我们正在努力解决两个不同的问题.

第一:

public List<Employees> GetAllEmployees()
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

此方法应由具有角色"HR"的授权用户调用.

第二个:

public Order GetMyOrder(int orderId)
{
    // do sutff
}
Run Code Online (Sandbox Code Playgroud)

此方法只应由订单所有者调用.

我知道为每种方法实现安全性很容易,例如:

public List<Employees> GetAllEmployees()
{
    // check if the user is in Role HR
}
Run Code Online (Sandbox Code Playgroud)

要么

public Order GetMyOrder(int orderId)
{
    // check if the order.Owner = user
}
Run Code Online (Sandbox Code Playgroud)

我正在寻找的是一些模式/最佳实践,以通用的方式实现这种安全性(每次都不编码if if else)我希望你明白我的意思:-)

Ste*_*ven 9

用户@mdma描述了一些关于面向方面编程的内容.为此,您需要使用外部库(例如伟大的PostSharp),因为.NET没有太多的AOP功能.但是,.NET已经具有基于角色的安全性的AOP机制,可以解决部分问题.请看以下标准.NET代码示例:

[PrincipalPermission(SecurityAction.Demand, Role="HR")]
public List<Employees> GetAllEmployees()
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

PrincipalPermissionAttribute是位于System.Security.Permissions命名空间的一部分,并且是.NET(因为.NET 1.0)的一部分.多年来我一直在使用它来在我的Web应用程序中实现基于角色的安全性.关于这个属性的好处是.NET JIT编译器在后台为你完成所有的编织,你甚至可以在类级别上定义它.在这种情况下,该类型的所有成员都将继承该属性及其安全设置.

当然它有其局限性.您的第二个代码示例无法使用基于.NET角色的安全属性实现.我认为你不能真正在这个方法中进行一些自定义安全检查,或者调用一些内部安全库.

public Order GetMyOrder(int orderId)
{
    Order o = GetOrderInternal(orderId);
    BusinessSecurity.ValidateOrderForCurrentUser(o);
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用AOP框架,但您仍然需要编写一个特定于框架的属性,该属性将再次调用您自己的安全层.只有当这样的属性替换多个方法调用时才会有用,例如当必须将代码放在try,catch,finally语句中时.当您进行简单的方法调用时,单个方法调用或单个属性IMO之间没有太大区别.

当您返回一组对象并希望过滤掉当前用户没有适当权限的所有对象时,LINQ表达式树可以派上用场:

public Order[] GetAllOrders()
{
    IQueryable orders = GetAllOrdersInternal();
    orders = BusinessSecurity.ApplySecurityOnOrders(orders);
    return orders.ToArray();
}

static class BusinessSecurity
{
    public static IQueryable<Order> ApplySecurityOnOrders(
       IQueryable<Order> orders)
    {
        var user = Membership.GetCurrentUser();

        if (user.IsInRole("Administrator"))
        {
            return orders;
        }

        return 
            from order in orders
            where order.Customer.User.Name == user.Name
            select order; 
    }
}
Run Code Online (Sandbox Code Playgroud)

当您的O/RM支持LINQ到表达式树(例如NHibernate,LINQ to SQL和Entity Framework)时,您可以编写一次这样的安全方法并将其应用到任何地方.当然,关于这一点的好处是,对数据库的查询将始终是最佳的.换句话说,不会检索到比需要更多的记录.

更新(多年后):

我在代码库中使用了这个属性很长一段时间,但几年前,我得出结论,基于属性的AOP有可怕的缺点.例如,它阻碍了可测试性.由于安全代码是使用普通代码编写的,因此无需模拟有效用户就无法运行正常的单元测试.这很脆弱,不应该成为单元测试的关注点(单元测试本身违反了单一责任原则).除此之外,它还会强迫您使用该属性丢弃代码库.

因此PrincipalPermissionAttribute,我宁愿通过使用装饰器包装代码来应用跨安全问题,而不是使用.这使我的应用程序更灵活,更容易测试.在过去的几年里,我写了几篇关于这种技术的文章(比如这一篇这篇文章).