2010-06-09 58 views
8

我们使用ASP.NET进行大量AJAX“Page Method”调用。 页面中定义的WebServices会从我们的BusinessLayer调用方法。 为了防止黑客调用Page方法,我们希望在BusinessLayer中实现一些安全性。制定业务层安全的方法。最佳做法/最佳模式

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

第一招:

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 then每次) 我希望你明白我的意思:-)

+1

我看到你已经在SO一段时间了。不过,我会建议您将标签([.net/c#])留在标题之外。让他们留在标签中。此外,“嗨”和“谢谢”虽然适合讨论论坛,但并不适用于SO等问答站点。谢谢。 – 2010-06-10 04:28:59

+0

@John好的。感谢提示。 – gsharp 2010-06-10 06:30:25

回答

9

User @ mdma描述了一些关于面向方面编程的内容。为此,您需要使用外部库(例如伟大的PostSharp),因为.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,您可以编写一次这样的安全方法并将其应用到任何地方。当然,关于这一点的好处是,对数据库的查询总是最优的。换句话说,没有更多的记录将被检索超过需要。

UPDATE(年后):

我用这个属性在我的代码基础很长一段时间,但几年后,我来到了这个属性基于AOP有可怕的缺点的结论。例如,它阻碍了可测试性。由于安全代码是用普通代码编织的,因此无法运行正常的单元测试而无需模拟有效的用户。这是脆弱的,不应该成为单元测试的考虑因素(单元测试本身违反单一职责原则)。除此之外,它强制你用这个属性抛弃你的代码库。

因此,我宁愿用decorators来包装代码,而不是使用PrincipalPermissionAttribute来代替安全性。这使得我的应用程序更加灵活并且更容易测试。在过去的几年中,我已经写了几篇关于这种技术的文章(例如this onethis one)。

0

如果您使用SOA,您可以创建一个安全服务,并且每个动作(方法)都会发送它的上下文(UserId,OrderId等)。安全服务知道有关业务安全规则。

方案可能是这样的

UI -> Security -> BLL -> DAL 
+0

任何样品/文献? – gsharp 2010-06-10 06:33:34

+1

我同意将安全性置于应用程序级别,而不是像其他答案那样在“框架”级别。如果角色是动态的呢? – 2010-06-16 16:13:45

2

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

以下文章列出了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(); 

您可以直接把属性对您的业务方法,紧耦合,或者创建具有这些属性的服务代理,所以安全细节保持独立。

+0

在我看来,解决方案与框架太紧密(通过装饰方法)。我将实现作为系统一部分的安全功能,因为它不像业务逻辑之外那样。我喜欢代理建议虽然..我2美分。 – 2010-06-16 16:18:34

+0

@Mike - 属性是你自己的,所以我不明白这是如何绑定到任何框架。它们是实现细节 - 声明式安全而不是编写代码。你仍然可以自由地编写代码,但是将代码放在业务对象本身上是OP说他想避免的。当这些属性放在业务对象上时,它们仍然与代码“分离” - 即易于识别。如果您将安全检查作为代码并将其放入业务逻辑中,情况并非如此。 – mdma 2010-06-16 16:41:58

+0

嗨,我看到它就像硬编码。这就是为什么我说“框架”很抱歉。如果角色是动态的呢?我会创建像“User”对象一样的安全对象,因为它们与其他任何东西一样是系统的一部分。 – 2010-06-16 19:27:30