ASP.NET: dependency injection and roles
I have a page using an injected BLL service: a simple service returning a set of objects with a function like this:
public IMyServic开发者_高级运维e { 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?
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.
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.
精彩评论