Шаблоны авторизации службы WCF

Я реализую безопасную службу WCF. Аутентификация выполняется с использованием имени пользователя / пароля или учетных данных Windows. Служба размещается в процессе службы Windows. Теперь я пытаюсь найти лучший способ реализовать авторизацию для каждой операции службы.

Например, рассмотрим следующий метод:

public EntityInfo GetEntityInfo(string entityId); 

Как вы знаете, в WCF есть объект OperationContext, из которого вы можете получить учетные данные безопасности, переданные вызывающим / клиентом. Теперь аутентификация уже была завершена к моменту, когда вызывается первая строка в методе. Однако как мы реализуем авторизацию, если решение зависит от самих входных данных? Например, в приведенном выше случае, скажем, пользователи «admin» (чьи разрешения и т. Д. Хранятся в базе данных) разрешены для получения информации об объекте, а другим пользователям не разрешается … где мы ставим проверки авторизации?

Скажем, мы помещаем его в первую строку метода следующим образом:

 CheckAccessPermission(PermissionType.GetEntity, user, entityId) //user is pulled from the current OperationContext 

Теперь есть несколько вопросов:

  1. Проверяем ли объект entityId (например, проверяем значение null / пустое значение и т. Д.) ДО проверки авторизации или INSIDE авторизации? Другими словами, если проверки авторизации должны быть включены в каждый метод, это хороший шаблон? Что должно произойти в первую очередь – подтверждение или авторизация аргументов?

  2. Как мы тестируем службу WCF, когда проверки авторизации находятся повсюду, как это, и мы не имеем OperationContext в модульном тесте !? (Предполагая, что я пытаюсь протестировать реализацию этого classа сервиса непосредственно без какой-либо настройки WCF).

Любые идеи парней?

На вопрос 1, сначала выполните авторизацию. Никакой код (внутри вашего контроля) не должен выполняться до авторизации для поддержания самой надежной безопасности. Приведенный выше пример Павла превосходный.

На вопрос 2 вы можете справиться с этим, выполнив подclassификацию вашей конкретной реализации службы. Сделайте настоящую бизнес-логику абстрактным classом с абстрактным методом «CheckPermissions», как вы упомянули выше. Затем создайте 2 подclassа, один для использования WCF и один (очень изолированный в неразвернутой DLL), который возвращает true (или что бы вы хотели сделать в тестировании устройства).

Пример (обратите внимание: они не должны быть в одном файле или даже в DLL!):

 public abstract class MyServiceImpl { public void MyMethod(string entityId) { CheckPermissions(entityId); //move along... } protected abstract bool CheckPermissions(string entityId); } public class MyServiceUnitTest { private bool CheckPermissions(string entityId) { return true; } } public class MyServiceMyAuth { private bool CheckPermissions(string entityId) { //do some custom authentication return true; } } 

Затем ваше развертывание WCF использует class «MyServiceMyAuth», и вы выполняете тестирование вашего устройства против другого.

Для вопроса 1 лучше всего сначала выполнить авторизацию. Таким образом, вы не отправляете сообщения об ошибках проверки неавторизованным пользователям.

BTW, вместо использования самодельного метода аутентификации (который, как я полагаю, является вашим CheckAccessPermission), вы можете подключиться к готовой поддержке WCF для поставщиков роли ASP.NET. Как только это будет сделано, вы выполните авторизацию через OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsInRole (). PrimaryIdentity – это IPrincipal.

Что касается вопроса № 2, я бы сделал это с помощью Dependency Injection и настроил вашу реализацию службы примерно так:

 class MyService : IMyService { public MyService() : this(new UserAuthorization()) { } public MyService(IAuthorization auth) { _auth = auth; } private IAuthorization _auth; public EntityInfo GetEntityInfo(string entityId) { _auth.CheckAccessPermission(PermissionType.GetEntity, user, entityId); //Get the entity info } } 

Обратите внимание, что IAuthorization – это интерфейс, который вы бы определили.

Поскольку вы собираетесь напрямую тестировать тип сервиса (т. Е. Не запускать его внутри фреймворка WCF), вы просто настраиваете свою службу на использование фиктивного типа IAuthorization, который позволяет все вызовы. Однако даже тест BETTER заключается в том, чтобы высмеять IAuthorization и тест, который он вызывается, когда и с параметрами, которые вы ожидаете. Это позволяет проверить, что ваши вызовы на методы авторизации действительны, а также сам метод.

Разделение авторизации на свой собственный тип также позволяет вам более легко проверить, что оно является правильным по отдельности. В моем (хотя и ограниченном) опыте использование шаблонов DI «дает вам намного лучшее разделение проблем и тестируемости в ваших типах, а также приводит к более понятному интерфейсу (это, очевидно, открыто для обсуждения).

Моя привилегированная фальсификация – это RhinoMocks, который является бесплатным и имеет очень приятный свободный интерфейс, но есть много других. Если вы хотите узнать больше о DI, вот некоторые хорошие праймеры и .Net frameworks:

  • Мартин Фаулер на DI
  • Джереми Миллер на DI
  • Список контейнерных контейнеров Scott Hanselman
  • Мой личный любимый контейнер DI: проект Castle Windsor Container