Пользователь @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). Я уже несколько лет использую его для реализации безопасности на основе ролей в моих веб-приложениях. Хорошая вещь об этом атрибуте состоит в том, что компилятор .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 поддерживает LINQ через деревья выражений (такие как NHibernate, LINQ to SQL и Entity Framework), вы можете написать такой метод защиты один раз и применять его везде. Конечно, приятно то, что запрос к вашей базе данных всегда будет оптимальным. Другими словами, не будет извлечено больше записей, чем необходимо.
ОБНОВЛЕНИЕ (годы спустя):
Я долгое время использовал этот атрибут в своей кодовой базе, но несколько лет назад я пришел к выводу, что AOP на основе атрибутов имеет ужасные недостатки. Например, это мешает тестированию. Поскольку код безопасности сплетен с обычным кодом, вы не можете запускать обычные модульные тесты без необходимости выдавать себя за действительного пользователя. Это хрупко и не должно быть предметом юнит-теста (сам юнит-тест нарушает принцип единой ответственности). Кроме того, он заставляет вас засорять вашу кодовую базу этим атрибутом.
Таким образом, вместо использования PrincipalPermissionAttribute
я предпочитаю сквозные вопросы, такие как безопасность, заключая код в декораторы . Это делает мое приложение намного более гибким и намного проще для тестирования. За последние несколько лет я написал несколько статей об этой технике (например, , этот и , этот ).