What is the clean way to pass my LoginContext down through the layers to the data access layer?
https://softwareengineering.stackexchange.com/questions/227671
-
02-10-2020 - |
题
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?
解决方案
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.