2015-01-29 19 views
6

Ho un controller in cui sto inserendo un'interfaccia di servizio nel costruttore. Il servizio ha anche interfacce iniettate nel suo costruttore. Il contenitore IoC (Unity) deve utilizzare le informazioni sull'utente durante la costruzione di una delle classi restituite per una determinata interfaccia.ASP.NET MVC Autentica prima del controller istanziato

Quello che sta accadendo è che il controllore viene istanziato prima che l'attributo [Autorizza] sia valutato e l'utente sia autenticato. Questo impone Unity per eseguire l'iniezione della dipendenza e utilizzare le informazioni sull'utente prima che abbiano effettuato l'accesso. Nessuno di questi era un problema quando stavamo usando l'autenticazione integrata di Windows, ma ora stiamo usando OpenID Connect in Azure AD e le informazioni dell'utente non sono fino a quando non si collegano (ciò accade dopo che il controller è stato istallato).

Ho sentito (in altri post) che esiste un modo per configurare la mia classe di avvio di Owin per spostare l'autenticazione in precedenza nel processo, ma non riesco a trovare alcun esempio su come farlo. Ho bisogno che l'autenticazione avvenga prima che il controller venga istanziato.

Ecco un esempio semplificato di ciò che ho ...

Controller:

[Authorize] 
public class MyController : Controller 
{ 
    private readonly IMyService myService; 

    public MyController(IMyService myService) 
    { 
     this.myService = myService; 
    } 

    // ... 
} 

configurazione di Unity:

public class UnityBootstrap : IUnityBootstrap 
{ 
    public IUnityContainer Configure(IUnityContainer container) 
    { 
     // ... 

     return container 
      .RegisterType<ISomeClass, SomeClass>() 
      .RegisterType<IMyService>(new InjectionFactory(c => 
      { 
       // gather info about the user here 
       // e.g. 
       var currentUser = c.Resolve<IPrincipal>(); 
       var staff = c.Resolve<IStaffRepository>().GetBySamAccountName(currentUser.Identity.Name); 
       return new MyService(staff); 
      })); 
    } 
} 

OWIN avvio (Startup.Auth.cs):

public void ConfigureAuth(IAppBuilder app) 
{ 
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); 

    app.UseCookieAuthentication(new CookieAuthenticationOptions()); 

    app.UseOpenIdConnectAuthentication(
     new OpenIdConnectAuthenticationOptions 
     { 
      ClientId = this.clientID, 
      Authority = this.authority, 
      PostLogoutRedirectUri = this.postLogoutRedirectUri, 
      Notifications = new OpenIdConnectAuthenticationNotifications 
      { 
       RedirectToIdentityProvider = context => 
       { 
        context.ProtocolMessage.DomainHint = this.domainHint; 
        return Task.FromResult(0); 
       }, 
       AuthorizationCodeReceived = context => 
       { 
        var code = context.Code; 

        var credential = new ClientCredential(this.clientID, this.appKey.Key); 
        var userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value; 
        var authContext = new AuthenticationContext(this.authority, new NaiveSessionCache(userObjectID)); 
        var result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, this.graphUrl); 
        AzureAdGraphAuthenticationHelper.Token = result.AccessToken; 
        return Task.FromResult(0); 
       } 
      } 
     }); 
} 
+0

ho inciampato su questo troppo, anche se senza alcuna delle cose DI. Necessario un ID utente nel costruttore del controller di base, e fu sorpreso di vedere il costruttore chiamato prima che'AuthorizedOttribute avesse spostato l'utente. –

+1

Una spiegazione del perché è così http://stackoverflow.com/a/4462767/10245 –

risposta

4

Af non riuscendo a trovare nulla che riguardasse specificamente il mio problema online, ho deciso di scavare nello ASP.NET MVC 5 application lifecycle. Ho scoperto che potevo creare un IControllerFactory personalizzato (o ereditato da DefaultControllerFactory) dove potevo definire il metodo CreateController.

Durante CreateController, controllo se l'utente è autenticato. Se lo sono, ho semplicemente permesso a DefaultController Factory di creare il controller come farebbe normalmente.

Se l'utente non è autenticato, creo il mio (molto) semplice controllore "Auth" al posto del controller richiesto (quello con i molti strati di dipendenze) con il RequestContext lasciato invariato.

Il controller di Auth verrà istanziato senza problemi poiché non ha dipendenze. Nota: nessuna azione viene mai eseguita sul controller Auth. Una volta creato il controller di Auth, viene attivato l'AuthorizeAttribute globale e l'utente viene indirizzato all'autenticazione (tramite OpenID Connect in Azure AD e ADFS).

Dopo l'accesso, vengono reindirizzati alla mia app con il RequestContext originale ancora intatto. CusomControllerFactory vede l'utente come autenticato e viene creato il controller richiesto.

Questo metodo funziona alla grande per me come i miei controllori hanno una grande catena di dipendenza viene iniettato (cioè controller dipende ISomeService che dipende molti ISomeRepository, ISomeHelper, ISomethingEles ...) e nessuna delle dipendenze sono risolti fino a quando l'utente è login

Sono ancora decisamente aperto ad ascoltare altre idee (più eleganti) su come ottenere ciò che stavo chiedendo nella mia domanda iniziale.


CustomControllerFactory.cs

public class CustomControllerFactory : DefaultControllerFactory 
{ 
    public override IController CreateController(RequestContext requestContext, string controllerName) 
    { 
     var user = HttpContext.Current.User; 
     if (user.Identity.IsAuthenticated) 
     { 
      return base.CreateController(requestContext, controllerName); 
     } 

     var routeValues = requestContext.RouteData.Values; 
     routeValues["action"] = "PreAuth"; 
     return base.CreateController(requestContext, "Auth"); 
    } 
} 

Global.asax.cs

public class MvcApplication : HttpApplication 
{ 
    protected void Application_Start() 
    { 
     // ... 
     ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory)); 
    } 

    // ... 
} 

AuthController.cs

public class AuthController : Controller 
{ 
    public ActionResult PreAuth() 
    { 
     return null; 
    } 
} 
Problemi correlati