Безопасность на уровне данных в MVC 3

#.net #asp.net-mvc-3

#.net #asp.net-mvc-3

Вопрос:

Мне интересно узнать о наилучшем подходе к безопасности на уровне данных в MVC 3. Позвольте мне нарисовать картину.

Существует представление событий. В этом событии указывается название события и список игроков, участвующих в этом событии.

В зависимости от отношения текущего пользователя к этому событию, каждый пользователь получит существенно различное представление.

Например, если пользователь является организатором, то пользователь может просматривать и управлять доступностью всех других пользователей для этого события.

Если пользователь просто участвует в этом событии, то, очевидно, что пользователь может управлять только своей собственной доступностью.

Существует больше комбинаций, чем эта.

Я знаю о пользовательских фильтрах действий, но это кажется излишним.

Вместо этого я выбрал подход, при котором в индексе события есть оператор switch, который перенаправляет на соответствующее представление, например, OrganizerEventView или PlayerEventView.

Это самое простое. Я думаю.

Где это становится беспорядочным, так это то, что я использовал общий редактор для перечисления моделей игроков (часть модели основного представления), чтобы перечислить игроков. Сам этот общий редактор также должен учитывать безопасность на уровне данных.

Я на правильном пути, или есть лучший способ?

Комментарии:

1. Привет, ваш вопрос интересный, но довольно абстрактный. Не могли бы вы привести несколько подробных примеров?

Ответ №1:

Дункан,

Это то, с чем я сталкиваюсь в каждом приложении. Вот некоторый контекст: я работаю на одном факультете в крупном университете, поэтому нам приходится иметь дело с несколькими источниками данных. Большинство наших приложений имеют систему безопасности, которая объединяет университетские «центральные ИТ» службы безопасности, такие как централизованная аутентификация и Active Directory, а также «домашние» роли и разрешения.

Наши приложения должны представлять разные представления и действия для разных пользователей на основе данных как в «домашних» приложениях (используемых внутри нашего отдела), так и в корпоративных данных.

Наш подход: после тщательного изучения и не найдя нигде хорошего решения этой проблемы, я закончил тем, что написал структуру безопасности для нашего использования. Я мог бы также продолжить и описать, что он делает здесь. Вот что требуется:

  1. Статические и динамические роли. Статические роли не зависят от данных (например, я получаю роль разработчика в приложениях), в то время как динамические роли зависят от SecurityContext (например, финансовый сотрудник отдела получает роль «Финансовый сотрудник», когда она просматривает учетные записи своего отдела).
  2. Разрешения, которые назначаются статическим или динамическим ролям.
  3. SecurityContext, который инкапсулирует все данные, необходимые для выполнения проверки разрешений, включая текущего пользователя и любые данные (например, номера счетов, идентификаторы документов предложения, даты транзакций, что угодно).
  4. SecurityContextValidator, который принимает SecurityContext и возвращает набор или роли, которые действительны для указанного пользователя в указанном SecurityContext. Итак, логика, которая определяет, кто может видеть, что находится в этом классе.
  5. Один SecurityContextValidator для каждого SecurityContext. Это сопоставление регистрируется при инициализации с помощью SecurityContextManager. Я использую Ninject для загрузки моего модуля безопасности, который выполняет это при запуске.
  6. Контекст по умолчанию, используемый, если разрешение не указывает SecurityContext, который просто содержит основную информацию из Asp.Net безопасность.
  7. Служба безопасности, которая предоставляет пользователю SecurityContext и разрешение, определяет все роли, которые пользователь имеет в этом SecurityContext, и проверяет, есть ли у какой-либо из ролей проверяемое разрешение.

На данный момент, вот пример потока в Asp.Net MVC:

  • Получение HttpRequest
  • Аутентификация с помощью проверки подлинности форм
  • Маршрут к действию контроллера
  • если (разрешение.getByName(«CanDoSomething»).isAllowed()) { // продолжить

— [Внутреннее разрешение.Разрешено] —

  • SecurityService получает SecurityContext
  • Найдите SecurityContextValidator для указанного SecurityContext
  • Агрегируйте все статические и динамические роли из SecurityContextValidator
  • Перебирайте роли и проверяйте, запрашивается ли у какой-либо из них разрешение.
  • Верните значение True или False!

Чтобы упростить задачу, я сделал еще один шаг вперед и создал атрибут AbstractContextProtectedAttribute, который ожидает делегата SecurityContextFactory, который может создать SecurityContext (например, с помощью HttpRequest) и разрешение на проверку с помощью указанного SecurityContext. Затем подклассы этого класса можно использовать для оформления действий контроллера.

Фу! Итак, все это позволяет мне теперь настроить таблицу пользователей, ролей, разрешений и сопоставить их друг с другом, определить все разрешения в базе данных. Я написал подключаемый SecurityPersistenceService, который делает инфраструктуру безопасности независимой от используемой стратегии сохранения — к сожалению, у нас есть все, начиная с чтения данных, адаптеров данных и заканчивая Linq2SQL и EF. Но, по крайней мере, мы можем написать такой код:

 [Protected("CanAccessX")] // Checks using default context
public class SomeController 
{
    [Protected("CanSeeY")] // Checks using default context
    public PartialViewResult GetY(<parameters>) 
    {
        var canSeeY_Variation1 = Permission.Get("CanSeeY_Variation1") ;
        var y_Variation1_Context = new Y_Variation1_Context { <build your context here> } ;
        if (canSeeY_Variation1.IsAllowed(y_variation1_Context))
        {
            <return variation 1 view>
        }

        // Y_Variation_2...etc
    }
}
  

И чтобы это сработало, при запуске я регистрирую соответствующие валидаторы:

 public class MyNinjectModule
{
    public override void Load()
    {
        // Wire up a persistence service for the security framework
        // to use.
        SecurityService.SecurityPersistenceService = new MySecurityPersistenceServiceImplementation() ;

        // This is what allows the SecurityService to figure out what Validator to use
        // in a specified Context to get the User's Roles.          
        SecurityService.RegisterValidator<Y_Variation1_Context>(new Y_Variation1_ContextValidator(...)) ;
    }
}
  

Сейчас я работаю над дополнением к этой платформе, которое позволяет мне выполнять эти проверки над перечислимыми данными, тем самым наполняя все объекты моего домена инфраструктурой безопасности. Единственный известный мне чистый способ сделать это — использовать AOP. Раньше я работал на Java и использовал AspectJ. Теперь я рассматриваю PostSharp.

Надеюсь, это дает представление о вашей проблеме.

Комментарии:

1. Также я бы посоветовал вам взглянуть на архитектуру управления идентификацией Kuali Rice framework: kuali.org/rice Это Java, но они обеспечивают безопасность на уровне данных в веб-приложении MVC, так что там есть несколько хороших уроков.

Ответ №2:

Ответ будет меняться в зависимости от размера, сложности и прогнозируемого роста вашего приложения. При наличии безопасности в контроллере, а затем при работе с разными представлениями существуют компромиссы. Например, репликация кода / разметки может быть недостатком. Для сложных приложений портлеты могут помочь, но это тяжелая работа. Представление может меняться в зависимости от ролей безопасности и доступа, вы бы передали параметр / данные с контроллера точно так же, как и все остальное, если оно еще не доступно через сеанс. Я бы создал служебный метод для использования представлений, чтобы логика / правила не попадали в представление.

Комментарии:

1. Мне нравится идея служебного метода, можете ли вы привести краткий пример, если это не слишком сложно?

2. Вы имеете в виду метод @helper?

3. аннотации — это один из способов реализации утилиты, вы можете просто использовать Util.method() или повеселиться с Spring AOP. Просто одно простое место / вызов.

Ответ №3:

Насколько я понимаю ваш вопрос, безопасность на основе ролей — это правильный путь.

Если они являются организатором, то они являются ролью организатора. Однако вам нужно немного абстрагироваться от этого. Поскольку роль организатора теоретически будет организатором всех других событий, вам нужен метод, который выполняет это определение и заполняет роли по запросу, чтобы пользователь A был организатором события A, но не организатором события B. В идеале это должно произойти до обращения к коду контроллера, поэтому ваш выбор — global.asax.cs или фильтр авторизации.

Комментарии:

1. Спасибо, я проведу расследование. Это звучит немного странно для моего простого решения, но я считаю, что это очень масштабируемое решение для более сложной системы

Ответ №4:

Использование ролей и пользовательских атрибутов ролей кажется подходящим (хотя я могу сказать, что без кода или лучшего понимания вашей кодовой базы никто не сможет предложить вам «лучший» способ) для вашей проблемы.

Комментарии:

1. Однако это не зависит от ролей, а на уровне данных. Так, например, конкретный пользователь может быть, в зависимости от данных, организатором события 1, но просто участником события 2

2. Тогда кажется, что пользовательский фильтр действий, основанный на владельце события / других критериях, является лучшим способом.