How to pass Current User Information to all Layers

2019-01-07 10:48发布

Similar questions have been asked before but not quite the same (unless I missed it)

I want to pass IUserInfo class instance through my Service, Domain , Domain Events, Domain Event Handlers...

Whats is the best way to do it.

Should I

  • Inject it using IoC by registering it against instance of Httpcontext.Current.session["CurrentUser"];

  • Add the data to Current Thread.

  • Any other way

I am stuck at Domain Event Handlers where I want to use the data for auditing as well as sending emails.

I want to be able to use the CurrentUser information from almost anywhere in my application.

With threading as threads are pooled I am skeptical if the reuse of threads will reset the data. If not please shopw me how to use threading to pass IUser instance.

Regards,

Mar

2条回答
可以哭但决不认输i
2楼-- · 2019-01-07 11:09

My approach might not be ideal, but I find it working quite well. Thing what I did - I decided not to use dependency injection pass current user everywhere directly because that was getting too cumbersome and switched to static context. Problem with contexts - they are a bit difficult to manage.

This one is defined in my domain:

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

Note that delegate is static - for whole app only one at a time. And I'm not 100% sure about it's life cycle, possible memory leaks or whatnot.

Client application is responsible to initialize context. My web application does that on every request:

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

Using mvcextensions library to stream-line bootstrapping tasks. You can just subscribe for according events in global.asax for that.

In client side (web app), I implement application service named 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;
}

There is some more lame code necessary in order to use forms auth with roles w/o membership provider and role provider. But that's not the point of this question.

At domain level - I'm explicitly describing permissions that users might have like this one:

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.");
  }
}

Cool thing is - those permissions can be made more sophisticated. E.g.:

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.");
    }
  }

Permissions can be checked vica versa too - User has these fellas:

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

Here's my aggregate root base class:

public class Root:Entity,IRoot{
  public virtual void Authorize(IUserRights rights){
    UserContext.Current.CheckRightsFor(rights);
  }
}

And here's how I check permissions:

public class Application{
  public virtual void Accept(){
    Authorize(new AcceptApplications());
    OpeningStatus=OpeningStatus.Accepted;
  }
}

I hope that helps...

查看更多
Bombasti
3楼-- · 2019-01-07 11:23

I've done this kind of thing before using IoC. The benefit of this is that it's very testable -- you can stub out your user info for testing -- and reasonably readable and easy to follow.

查看更多
登录 后发表回答