我们使用ASP.NET和大量的AJAX“页面方法”调用。页面中定义的WebServices从我们的BusinessLayer中调用方法。为了防止黑客调用页面方法,我们希望在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 else进行编码),我希望你能理解我的意思:-)
发布于 2010-06-16 18:43:03
User @mdma描述了一些面向方面的编程。为此,您需要使用一个外部库(比如强大的AOP ),因为.NET没有太多的PostSharp功能。但是,.NET已经为基于角色的安全性提供了一种面向方面的机制,可以部分解决您的问题。请看下面的标准.NET代码示例:
[PrincipalPermission(SecurityAction.Demand, Role="HR")]
public List<Employees> GetAllEmployees()
{
// do stuff
}PrincipalPermissionAttribute是System.Security.Permissions名称空间的一部分,也是.NET的一部分(从.NET 1.0开始)。我已经使用它多年来在我的web应用程序中实现基于角色的安全性。这个属性的好处是.NET即时编译器在后台为您完成了所有的编织工作,您甚至可以在类级别上定义它。在这种情况下,该类型的所有成员都将继承该属性及其安全设置。
当然,它也有其局限性。您的第二个代码示例不能使用.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和实体框架)支持LINQ时,您可以编写一次这样的安全方法,然后将其应用到任何地方。当然,这样做的好处是,对数据库的查询总是最优的。换句话说,除非需要,否则不会检索更多记录。
更新(多年后):
我在我的代码库中使用了这个属性很长一段时间,但几年前,我得出结论,基于属性的AOP有可怕的缺点。例如,它阻碍了可测试性。由于安全代码是用普通代码编织而成的,因此如果不模拟有效用户,就无法运行普通单元测试。这是脆弱的,不应该成为单元测试的问题(单元测试本身违反了单一责任原则)。除此之外,它还会迫使您在代码库中使用该属性。
因此,我不使用PrincipalPermissionAttribute,而是通过用decorators包装代码来应用横切关注点,比如安全性。这使得我的应用程序更加灵活,测试起来也更加容易。在过去的几年里,我写了几篇关于这项技术的文章(例如this one和this one)。
发布于 2010-06-13 19:06:29
一个“最佳实践”是实现安全方面。这使安全规则与主要业务逻辑分离,避免了硬编码,并使在不同环境中更改安全规则变得容易。
下面的文章列出了实现方面和保持代码分离的7种方法。一种简单且不会更改业务逻辑接口的方法是使用代理。这将公开与您当前相同的接口,但允许另一种实现,它可以装饰现有的实现。可以使用硬编码或自定义属性将安全需求注入此接口。代理拦截对业务层的方法调用,并调用适当的安全检查。通过代理实现拦截在这里有详细描述-- Decouple Components by Injecting Custom Services into your Object's Invocation Chain。在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();您可以将属性直接放在您的业务方法上,紧密耦合,或者使用这些属性创建一个服务代理,以便将安全细节保持独立。
发布于 2010-06-10 12:20:23
如果您正在使用面向服务的体系结构,您可以创建一个Security Service,并且每个操作(方法)都将发送它的上下文(UserId、OrderId等)。安全服务了解业务安全规则。
方案可能是这样的
UI -> Security -> BLL -> DALhttps://stackoverflow.com/questions/3007337
复制相似问题