Pregunta

I have inherited an API implemented using ASP.NET WebApi 2. The actions on the controllers are all like this:

public object Get(long id) {
    LoginContext loginDetails = GetLoginDetails();
    if (loginDetails.IsAuthorised) {
        return _dependency.DoSomething(loginDetails, id);
    }

    return new HttpResponseMessage(HttpStatusCodes.Unauthorised);
}

The _dependency will have many methods all with similar signatures, and it will have dependencies of its own, and those will also use the LoginContext class until you finally reach the bottom of the call stack at the data access layer, where the LoginContext class is actually used. Dependencies are currently all injected into the constructor by the IoC container.

So there are a number of issues here that bother me - the repetitive checking in each controller action that the user is authorized, and the need to have a LoginContext on every method of every dependency referenced anywhere by the controller. Now in the first case, I have created an action filter that handles the authentication, and writes a custom identity (which contains the LoginContext details) back to the HttpContext.

That then leaves the meat of my question - what is the best way to pass my LoginContext down through the layers to the data access layer?

UPDATE: just to clarify, in response to some of the questions below, authentication itself is not being checked by the data access layer (although the business layer will obviously do things differently based on the caller's authorisation claims); but rather we are passing data gathered during the authentication process to the data access layer, where it is then being used to access particular resources, or for infrastructure concerns such as auditing. The problem still remains though, should every method of my business layer and every method of my data layer, take a LoginContext as one of its parameters, or are there better ways?

¿Fue útil?

Solución

The clean and appropriate way to do this is to use the request context scoped credentials.

What I mean here relies on two pieces of knowledge - first in ASP.NET there is a context scoped to the particular request; ASP.NET creates it at the beginning of the request and kills it once that request has completed and sent the response to the requesting client.

The context scoped to the HTTP request is accessible at: System.Web.HttpContext.Current

Secondly, this context has on it an IPrincipal which you can set in the property User, this is what I refer to above as credentials scoped to the request context.

So at the beginning of each request, often in the Global.asax (or HttpApplication wherever you implement it if not in your Global.asax) the AuthenticateRequest event is fired for the explicit purpose of letting you authenticate the given request. This is the normal place to put code which creates the IPrincipal and fills in it's IIdentity and places it on the System.Web.HttpContext.Current.User property.

After you fill in that User property, it's available (thread safely) to the rest of that particular request allowing all lower layers to know which user is executing the current request.

Licenciado bajo: CC-BY-SA con atribución
scroll top