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

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

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

第一:

public List GetAllEmployees() { // do stuff } 

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

第二个:

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

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

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

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

要么

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

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

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

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

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

当然它有其局限性。 您的第二个代码示例无法使用基于.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 ApplySecurityOnOrders( IQueryable 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支持LINQ到表达式树(例如NHibernate,LINQ to SQL和Entity Framework)时,您可以编写一次这样的安全方法并将其应用到任何地方。 当然,关于这一点的好处是,对数据库的查询始终是最佳的。 换句话说,不会检索到比需要更多的记录。

更新(多年后):

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

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

一个“最佳实践”是实现安全性方面。 这使安全规则与主要业务逻辑分离,避免了硬编码,并且可以轻松地在不同环境中更改安全规则。

下面的文章列出了7种实现方面和保持代码分离的方法。 一种简单且不会改变业务逻辑接口的方法是使用代理。 这暴露了与您当前相同的接口,但允许替代实现,它可以修饰现有实现。 可以使用硬编码或自定义属性将安全要求注入此接口。 代理拦截对业务层的方法调用,并调用相应的安全检查。 这里详细描述了通过代理实现拦截 – 通过将自定义服务注入对象的调用链来解耦组件 。 其他AOP方法在.NET中理解AOP中给出。

这是一个论坛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 GetAllEmployees(); 

您可以将属性直接放在业务方法上,紧密耦合,或者使用这些属性创建服务代理,因此安全性详细信息将保持独立。

如果您使用的是SOA,则可以创建安全服务,每个操作(方法)都会发送它的上下文(UserId,OrderId等)。 安全服务了解业务安全规则。

Scheme可能是这样的

 UI -> Security -> BLL -> DAL