ASP.NET: внедрение зависимостей и ролей - PullRequest
2 голосов
/ 20 июля 2010

У меня есть страница, использующая внедренный сервис BLL: простой сервис, возвращающий набор объектов с такой функцией:

public IMyService { List<Foo> All(); }

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

Где я могу настроить свою страницу для использования второй реализации?

Мое первое решение - поместить зависимость на IUnityContainer на странице и использовать ее для разрешения зависимости:

[Dependency]
public IUnityContainer Container { get; set;}

Page_Init(..) 
{ 
    _myService = User.IsInRole(MyRoles.Administrators)
                 ? Container.Resolve<IMyService>("forAdmins")
                 : Container.Resolve<IMyService>();
}

Но это очень уродливо: это ServiceLocator и оно не масштабируемони один из тестируемых.

Как я могу справиться с этой ситуацией?Может быть, создать дочерний контейнер для каждой роли?

Ответы [ 2 ]

4 голосов
/ 20 июля 2010

Вы можете реализовать его как комбинацию Декоратор и Композитный :

public SelectiveService : IMyService
{
    private readonly IMyService normalService;
    private readonly IMyService adminService;

    public SelectiveService(IMyService normalService, IMyService adminService)
    {
        if (normalService == null)
        {
            throw new ArgumentNullException("normalService");
        }
        if (adminService == null)
        {
            throw new ArgumentNullException("adminService");
        }

        this.normalService = normalService;
        this.adminService = adminService;
    }

    public List<Foo> All()
    {
        if(Thread.CurrentPrincipal.IsInRole(MyRoles.Administrators))
        {
            return this.adminService.All();
        }
        return this.normalService.All();
    }
}

Это соответствует принципу одиночной ответственности , поскольку каждыйреализация делает только одно.

1 голос
/ 20 июля 2010

Я согласен с вами, что ваш нынешний дизайн ужасен.Что мне лично не нравится в этом подходе, так это то, что вы настраиваете конфигурацию безопасности на странице.У вас будет ошибка безопасности, когда кто-нибудь забудет об этом и как вы проверяете правильность конфигурации этой страницы?

Вот две идеи: Во-первых: используйте фабрику, способную разрешить правильную реализацию этой службы на основедля пользовательских ролей:

public static class MyServiceFactory
{
    public static IMyService GetServiceForCurrentUser()
    {
        var highestRoleForUser = GetHighestRoleForUser();

        Container.Resolve<IMyService>(highestRoleForUser);
    }

    private static string GetHighestRoleForUser()
    {
        var roles = Roles.GetRolesForUser().ToList();
        roles.Sort();
        return roles.Last();
    }
}

Во-вторых: есть несколько методов на этом интерфейсе, один для обычных пользователей, один для администраторов.Реализация этого интерфейса может иметь PrincipalPermissionAttribute, определенный для ограниченных методов:

class MyServiceImpl : IMyService
{
    public List<Foo> All()
    {
       // TODO
    }

    [PrincipalPermission(SecurityAction.Demand, Role ="Administrator")]
    public List<Foo> AllAdmin()
    {
       // TODO
    }
}

Надеюсь, это поможет.

...