代码之家  ›  专栏  ›  技术社区  ›  gsharp

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

  •  9
  • gsharp  · 技术社区  · 15 年前

    我们使用的ASP.NET中有很多Ajax“页面方法”调用。 页面中定义的WebServices从我们的业务层调用方法。 为了防止黑客调用页面方法,我们希望在业务层中实现一些安全性。

    我们正在解决两个不同的问题。

    第一个:

    public List<Employees> GetAllEmployees()
    {
        // do stuff
    }
    

    此方法应由具有“HR”角色的授权用户调用。

    第二种:

    public Order GetMyOrder(int orderId)
    {
        // do sutff
    }
    

    此方法只能由订单的所有者调用。

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

    public List<Employees> GetAllEmployees()
    {
        // check if the user is in Role HR
    }
    

    public Order GetMyOrder(int orderId)
    {
        // check if the order.Owner = user
    }
    

    我要寻找的是一些模式/最佳实践,以通用的方式实现这种安全性(不必每次都对if-then-else进行编码)。 我希望你明白我的意思——)

    3 回复  |  直到 10 年前
        1
  •  9
  •   Steven    10 年前

    user@mdma介绍了一些面向方面的编程。为此,您需要使用外部库(如Great Postshap),因为.NET没有太多的AOP功能。然而,.NET已经有了一个基于角色的安全性AOP机制,可以解决部分问题。请看下面的标准.NET代码示例:

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

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

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

    public Order GetMyOrder(int orderId)
    {
        Order o = GetOrderInternal(orderId);
        BusinessSecurity.ValidateOrderForCurrentUser(o);
    }
    

    当然,您可以使用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; 
        }
    }
    

    当您的O/RM通过表达式树(如nhibernate、linq to sql和entity framework)支持linq时,您可以编写一次这样的安全方法,并将其应用到任何地方。当然,这方面的好处是,对数据库的查询总是最佳的。换句话说,检索的记录不会超过所需的数量。

    更新(几年后):

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

    所以不用 PrincipalPermissionAttribute ,我宁愿通过将代码包装为 decorators . 这使我的应用程序更灵活,更容易测试。在过去的几年里,我写了几篇关于这种技术的文章(例如 this one this one )

        2
  •  2
  •   mdma    14 年前

    一个“最佳实践”是实现安全方面。这使得安全规则与主业务逻辑分离,避免了硬编码,并使在不同环境中更改安全规则变得容易。

    下面的文章列出了实现方面和保持代码独立的7种方法。一种简单且不会改变业务逻辑接口的方法是使用代理。这将公开与当前相同的接口,但允许使用可修饰现有实现的替代实现。可以使用硬编码或自定义属性将安全性需求注入到这个接口中。代理会截获对业务层的方法调用,并调用适当的安全检查。这里详细描述了通过代理实现拦截。- Decouple Components by Injecting Custom Services into your Object's Invocation Chain . 其他AOP方法见 Understanding AOP in .NET .

    这里有一个 forum post 讨论安全性作为一个方面,使用建议和安全属性实现。最终结果是

    public static class Roles 
    {
        public const string ROLE_ADMIN = "Admin";
        public const string ROLE_CONTENT_MANAGER = "Content Manager";
    }
    
    // business method    
    [Security(Roles.ROLE_HR)]
    public List<Employee> GetAllEmployees();
    

    您可以将该属性直接放在您的业务方法上,紧密耦合,或者使用这些属性创建服务代理,因此安全性细节是分开的。

        3
  •  0
  •   SqlRyan    15 年前

    如果您使用的是SOA,那么您可以创建一个安全服务,并且每个操作(方法)都将发送它的上下文(userid、orderid等)。安全服务了解业务安全规则。

    计划可能是这样的

    UI -> Security -> BLL -> DAL