Вопрос

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

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

public EntityInfo GetEntityInfo(string entityId);

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

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

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

Теперь есть пара вопросов:

  1. Проверяем ли мы идентификатор объекта (например, проверяем нулевое/пустое значение и т. д.) ПЕРЕД проверкой авторизации или ВНУТРИ проверки авторизации?Другими словами, если проверки авторизации должны быть включены в каждый метод, будет ли это хорошим шаблоном?Что должно произойти первым — проверка аргументов или авторизация?

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

Есть идеи, ребята?

Это было полезно?

Решение

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

Что касается вопроса 2, вы можете справиться с этим, создав подкласс вашей конкретной реализации сервиса.Сделайте настоящую реализацию бизнес-логики абстрактным классом с абстрактным методом CheckPermissions, как вы упомянули выше.Затем создайте 2 подкласса: один для использования 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 используется класс MyServiceMyAuth, и вы выполняете модульное тестирование другого класса.

Другие советы

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

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

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

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, который разрешает все вызовы.Однако еще ЛУЧШИЙ тест — это имитировать IAuthorization и проверять, что она вызывается тогда и с теми параметрами, которые вы ожидаете.Это позволяет вам проверить корректность вызовов методов авторизации, а также самого метода.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top