Мой подход может быть не идеальным, но я считаю, что он работает довольно хорошо.Что я и сделал - я решил не использовать внедрение зависимостей передавать текущего пользователя везде напрямую, потому что это становилось слишком громоздким и переключалось на статический контекст.Проблема с контекстами - ими немного сложно управлять.
Это определено в моем домене:
public static class UserContext{
private static Func<User> _getCurrentUser;
private static bool _initialized;
public static User Current{
get{
if(!_initialized)
throw new Exception("Can i haz getCurrentUser delegate?");
var user=_getCurrentUser();
return user??User.Anonymous;
}
}
public static void Initialize(Func<User> getCurrentUser){
_getCurrentUser=getCurrentUser;
_initialized=true;
}
}
Обратите внимание, что делегат статичен - для всего приложения только по одному за раз.И я не на 100% уверен в его жизненном цикле, возможных утечках памяти или еще чем-то подобном.
Клиентское приложение отвечает за инициализацию контекста.Мое веб-приложение делает это при каждом запросе:
public class UserContextTask:BootstrapperTask{
private readonly IUserSession _userSession;
public UserContextTask(IUserSession userSession){
Guard.AgainstNull(userSession);
_userSession=userSession;
}
public override TaskContinuation Execute(){
UserContext.Initialize(()=>_userSession.GetCurrentUser());
return TaskContinuation.Continue;
}
}
Использование библиотеки mvcextensions для оптимизации задач начальной загрузки.Вы можете просто подписаться на соответствующие события в global.asax для этого.
На стороне клиента (веб-приложение) я реализую службу приложений с именем IUserSession :
public User GetCurrentUser(){
if(HttpContext.Current.User==null) return null;
var identity=HttpContext.Current.User.Identity;
if(!identity.IsAuthenticated) return null;
var user=_repository.ByUserName(identity.Name);
if(user==null) throw new Exception("User not found. It should be. Looks bad.");
return user;
}
Для использования аутентификации форм с ролями без поставщика членства и поставщика ролей требуется еще код lame .Но суть этого вопроса не в этом.
На уровне домена - я подробно описываю разрешения, которые могут иметь пользователи, например:
public class AcceptApplications:IUserRights{
public bool IsSatisfiedBy(User u){
return u.IsInAnyRole(Role.JTS,Role.Secretary);
}
public void CheckRightsFor(User u){
if(!IsSatisfiedBy(u)) throw new ApplicationException
("User is not authorized to accept applications.");
}
}
Классная вещь - эти разрешения могут бытьсделал более изощренным.Например:
public class FillQualityAssessment:IUserRights{
private readonly Application _application;
public FillQualityAssessment(Application application){
Guard.AgainstNull(application,
"User rights check failed. Application not specified.");
_application=application;
}
public bool IsSatisfiedBy(User u){
return u.IsInRole(Role.Assessor)&&_application.Assessors.Contains(u);
}
public void CheckRightsFor(User u){
if(!IsSatisfiedBy(u))
throw new ApplicationException
("User is not authorized to fill quality assessment.");
}
}
Разрешения могут быть проверены и наоборот - у пользователя есть следующие пароли:
public virtual bool HasRightsTo<T>(T authorizationSpec) where T:IUserRights{
return authorizationSpec.IsSatisfiedBy(this);
}
public virtual void CheckRightsFor<T>(T authorizationSpec) where T:IUserRights{
authorizationSpec.CheckRightsFor(this);
}
Вот мой совокупный корневой базовый класс:
public class Root:Entity,IRoot{
public virtual void Authorize(IUserRights rights){
UserContext.Current.CheckRightsFor(rights);
}
}
Ивот как я проверяю права доступа:
public class Application{
public virtual void Accept(){
Authorize(new AcceptApplications());
OpeningStatus=OpeningStatus.Accepted;
}
}
Надеюсь, это поможет ...