15

Ich bin eine Web-API-Anwendung erstellen und ich möchte Träger Tokens für die Benutzerauthentifizierung verwenden. Ich habe die Token-Logik nach this post implementiert und alles scheint gut zu funktionieren. HINWEIS: Ich verwende den ASP.NET Identity Provider nicht. Stattdessen habe ich eine benutzerdefinierte Benutzer-Entität und Dienste für sie erstellt.Autofac Dependency-Injektion in der Implementierung von OAuthAuthorizationServerProvider

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     ConfigureOAuth(app); 

     var config = new HttpConfiguration(); 
     var container = DependancyConfig.Register(); 
     var dependencyResolver = new AutofacWebApiDependencyResolver(container); 
     config.DependencyResolver = dependencyResolver; 

     app.UseAutofacMiddleware(container); 
     app.UseAutofacWebApi(config); 

     WebApiConfig.Register(config); 
     app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
     app.UseWebApi(config); 
    } 

    public void ConfigureOAuth(IAppBuilder app) 
    { 
     var oAuthServerOptions = new OAuthAuthorizationServerOptions 
     { 
      AllowInsecureHttp = true, 
      TokenEndpointPath = new PathString("/token"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
      Provider = new SimpleAuthorizationServerProvider() 
     }; 

     // Token Generation 
     app.UseOAuthAuthorizationServer(oAuthServerOptions); 
     app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 

    } 
} 

und dies ist meine Umsetzung der SimpleAuthorizationServerProvider Klasse

private IUserService _userService; 
    public IUserService UserService 
    { 
     get { return (IUserService)(_userService ?? GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUserService))); } 
     set { _userService = value; } 
    } 

    public async override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
    { 
     context.Validated(); 
    } 

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

     var user = await UserService.GetUserByEmailAndPassword(context.UserName, context.Password); 

     if (user == null) 
     { 
      context.SetError("invalid_grant", "The user name or password is incorrect."); 
      return; 
     } 

     var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
     identity.AddClaim(new Claim("sub", context.UserName)); 
     identity.AddClaim(new Claim("role", "user")); 

     context.Validated(identity); 

    } 
} 

Nachdem ich das/Token-URL aufrufe, erhalte ich die folgenden Fehler

keinen Raum mit einem Tag Matching ‚AutofacWebRequest 'ist aus dem Bereich ersichtlich, in dem die Instanz angefordert wurde. Dies bedeutet im Allgemeinen, dass eine Komponente, die als HTTP-Anforderung registriert ist, von einer SingleInstance() - Komponente (oder einem ähnlichen Szenario) angefordert wird. Unter der Webintegration werden immer Abhängigkeiten von DependencyResolver.Current oder ILifetimeScopeProvider.RequestLifetime angefordert, niemals vom Container selbst

Gibt es eine Möglichkeit, Abhängigkeitsinjektion in dieser Klasse zu verwenden? Ich verwende ein Repository-Muster, um auf meine Entitäten zuzugreifen. Daher denke ich nicht, dass es eine gute Idee ist, eine neue Instanz des Objektkontextes zu erstellen. Was ist der richtige Weg, dies zu tun?

+0

Haben Sie es geschafft, eine Lösung zu finden? Ich habe das gleiche Problem und kann nicht ... danke. – shenku

+0

@Shenku: Ich habe eine Antwort hinzugefügt mit dem, was für mich funktioniert hat. Ich hoffe, es hilft. – jumuro

Antwort

8

Um die Dependency-Injektion in SimpleAuthorizationServerProvider zu verwenden, müssen Sie IOAuthAuthorizationServerProvider wie alle anderen Typen im Autofac-Container registrieren. Sie können etwas tun:

builder 
    .RegisterType<SimpleAuthorizationServerProvider>() 
    .As<IOAuthAuthorizationServerProvider>() 
    .PropertiesAutowired() // to automatically resolve IUserService 
    .SingleInstance(); // you only need one instance of this provider 

Sie müssen auch den Behälter mit dem ConfigureOAuth Methode übergeben und lassen Autofac Instanz wie dieses Problem zu beheben:

var oAuthServerOptions = new OAuthAuthorizationServerOptions 
{ 
    AllowInsecureHttp = true, 
    TokenEndpointPath = new PathString("/token"), 
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
    Provider = container.Resolve<IOAuthAuthorizationServerProvider>() 
}; 

Sie immer einzelne Instanzen, wenn Ihre Eigenschaften verwenden sollten innerhalb des Objekts nicht über externe Daten ändern (nehmen wir an, Sie haben eine Eigenschaft, die Sie in der Steuerung festgelegt, die abhängig von einigen Informationen in der Datenbank gespeichert ist - in diesem Fall sollten Sie InstancePerRequest verwenden).

+0

dann können Sie verwenden 'private readonly IAuthService _authService; öffentliche SimpleAuthorizationServerProvider (IAuthService AuthService) { _authService = authService; } ' – BotanMan

12

Ich hatte ein ähnliches Problem.

Das hier Problem ist, dass, wenn Sie versuchen IUserService in Ihrem Provider zu injizieren, erkennt Autofac, dass es als InstancePerRequest registriert wurde (das verwendet die bekannten Lebensdauer Anwendungsbereich Tag 'AutofacWebRequest'), aber die SimpleAuthorizationServerProvider im 'root' tainerbereich registriert ist wobei der Bereich 'AutofacWebRequest' nicht sichtbar ist.

Eine vorgeschlagene Lösung besteht darin, Abhängigkeiten als InstancePerLifetimeScope zu registrieren. Dies löste das Problem scheinbar, führte aber neue ein. Alle Abhängigkeiten sind im Bereich 'root' registriert, was bedeutet, dass für alle Anfragen die gleichen DbContext Instanzen und Dienste vorhanden sind. Steven erklärt sehr gut in diesem answer warum ist keine gute Idee, die DbContext zwischen Anfragen zu teilen.

Nach tieferer Untersuchung Aufgaben, habe ich das Problem, das 'AutofacWebRequest' vom OwinContext in der OAuthAuthorizationServerProvider Klasse immer gelöst und die Lösung der Dienste Abhängigkeiten von ihm, anstatt dass Autofac sie automatisch zu injizieren.

using Autofac.Integration.Owin; 
... 
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
{ 
    ... 
    // autofacLifetimeScope is 'AutofacWebRequest' 
    var autofacLifetimeScope = OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext); 
    var userService = autofacLifetimeScope.Resolve<IUserService>(); 
    ... 
} 

Ich habe OAuthAuthorizationServerProvider Registrierung und Injektion innerhalb ConfigureOAuth Verfahren in ähnlicher Weise hergestellt als von Laurentiu Stamate in another response auf diese Frage vorgeschlagen, wie: Dazu habe ich die OwinContextExtensions.GetAutofacLifetimeScope() Erweiterungsmethode von Autofac.Integration.Owin, siehe Beispiel unten verwendet SingleInstance(). Ich habe RefreshTokenProvider in der gleichen Weise implementiert.

EDIT

@BramVandenbussche, das ist meine Configuration Methode in der Startup Klasse, in der Sie die Reihenfolge der Middle sehen können zur OWIN Pipeline hinzugefügt:

public void Configuration(IAppBuilder app) 
{ 
    // Configure Autofac 
    var container = ConfigureAutofac(app); 

    // Configure CORS 
    ConfigureCors(app); 

    // Configure Auth 
    ConfigureAuth(app, container); 

    // Configure Web Api 
    ConfigureWebApi(app, container); 
} 
+1

OwinContextExtensions.GetAutofacLifetimeScope gibt null für mich zurück, gab es noch etwas, das Sie eingerichtet haben? –

+0

@BramVandenbussche, war Ihr Projekt vor Änderungen für den Aufruf dieser Methode? – jumuro

+1

@BramVandenbussche, rufen Sie 'app.UseAutofacMiddleware (Container)'? Wie von Alex Meyer-Gleaves in einem seiner [Posts] angedeutet (http://alexmg.com/owin-support-for-the-web-api-2-and-mvc-5-integrations-in-autofac/) : "Dies ist erforderlich, da neben der Aktivierung der Middleware-DI-Unterstützung ** auch der Gültigkeitsbereich der Lebensdauer im OWIN-Kontext platziert wird **". – jumuro

1

ich @jumuro auch versucht, antworte mit dem OwinContextExtensions.GetAutofacLifetimeScope, das meinen Tag speichert. Anstatt den IUserService zur Laufzeit zu registrieren, gibt diese Antwort eine Option zur Validierung/zum Erstellen des Instanzservice nach der Anforderung.

Ich habe eine neue Antwort hinzugefügt, weil ich wegen meines schlechten Rufs noch nicht kommentieren kann, aber zusätzliche Leitfäden hinzugefügt habe, um jemandem zu helfen.

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 

     try 
     { 
      if (service == null) 
      { 
       var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext); 
       service = scope.Resolve<IUserService>(); 
      } 
      var user = await service.FindUserAsync(context.UserName); 
      if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt)) 
      { 
       context.SetError("invalid_grant", "The user name or password is incorrect."); 
       return; 
      } 
     } 
     catch(Exception ex) 
     { 
      context.SetError("invalid_grant", ex.Message); 
      return; 
     } 

     var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
     identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); 

     AuthenticationProperties properties = CreateProperties(context.UserName); 
     AuthenticationTicket ticket = new AuthenticationTicket(identity, properties); 
     context.Validated(ticket); 
     context.Request.Context.Authentication.SignIn(identity); 

    } 
Verwandte Themen