ASP.NET:依赖注入和角色

发布于 2024-09-10 17:46:44 字数 641 浏览 9 评论 0原文

我有一个使用注入的 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,而且它既不可扩展又不可测试。

我该如何处理这种情况?也许为每个角色创建一个子容器?

I have a page using an injected BLL service: a simple service returning a set of objects with a function like this:

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

There is a default implementation for normal users.
Now, i need that users in administrative role can view more objects, with another implementation of the service.

Where can i configure my page to use the second implementation?

My first solution is to put the dependency to the IUnityContainer in the page, and use it to resolve the dependency:

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

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

But it's very ugly: it's a ServiceLocator and it's neither scalable neither testable.

How can i handle this situation? Maybe creating a child container for every role?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

清秋悲枫 2024-09-17 17:46:44

您可以将其实现为 DecoratorComposite 的组合:

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();
    }
}

这遵循单一职责原则,因为每个实现只做一件事。

You could implement it as a combination of Decorator and Composite:

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();
    }
}

This follows the Single Responsibility Principle since each implementation does only one thing.

戏舞 2024-09-17 17:46:44

我同意你的观点,你目前的设计很丑。我个人不喜欢这种方法的是,您在页面内设置安全配置。当有人忘记这一点时,您就会遇到安全错误,您如何测试此页面配置是否正确?

这里有两个想法:
第一的:
使用能够根据用户角色解决该服务的正确实现的工厂:

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
    }
}

我希望这会有所帮助。

I agree with you that your current design is ugly. What I personally dislike about this approach is that you are setting up the security configuration inside a page. You will have a security bug when anyone forgets this and how are you testing that this page configuration is correct?

Here are two ideas:
First:
Use a factory that is able to resolve the correct implementation of that service based on the user roles:

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();
    }
}

Second:
Have multiple methods on that interface, one for normal users, one for administrators. The implementation of that interface can have the PrincipalPermissionAttribute defined on the restricted methods:

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

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

I hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文