2014-09-23 48 views
17

新的依賴注入,所以這可能是一件簡單的事情,但我已經嘗試過,無法弄清楚,我正在使用簡單注入器。依賴注入(使用SimpleInjector)和OAuthAuthorizationServerProvider

我有一個WebApi,使用SimpleInjector完美罰款,現在我想實現安全使用OAuth。

要做到這一點,我開始學習本教程,這是非常有益的,但犯規利用扶養注射

http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

我有我的Global.asax文件看起來像這樣,設置扶養注射液(工作完美)

protected void Application_Start() 
{ 
    SimpleInjectorConfig.Register(); 

    GlobalConfiguration.Configure(WebApiConfig.Register); 
} 

我創建了一個Startup.Auth.cs文件來配置的OAuth

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     var OAuthServerOptions = new OAuthAuthorizationServerOptions() 
     { 
      AllowInsecureHttp = true, 
      TokenEndpointPath = new PathString("/token"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
      Provider = new MyAuthorizationServerProvider() // here is the problem 
     }; 

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

現在,正如我上面評論,MyAuthorizationServerProvider是問題。它需要我通常注入的IUserService參數。我不想清空構造函數,因爲我的IUserService還會注入一個存儲庫。這裏是文件

public class ApiAuthorizationServerProvider : OAuthAuthorizationServerProvider 
{ 
    private IUserService _service; 
    public ApiAuthorizationServerProvider (IUserService service) 
    { 
     _service = service; 
    } 

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

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

     IUserService service = Startup.Container.GetInstance<IUserService>(); 
     User user = _service.Query(e => e.Email.Equals(context.UserName) && 
      e.Password.Equals(context.Password)).FirstOrDefault(); 

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

    } 
} 

我怎麼能得到這與依賴注入工作?這一定會發生很多,必須能夠做一些事情來處理它。我相信它很簡單,但我仍然在學習。

+1

希望這有助於https://simpleinjector.codeplex。 com/discussion/564822 – DSR 2014-09-23 14:25:32

+0

您是否找到其他解決方案? – moyomeh 2016-02-22 17:15:59

+0

我使用來自github的OpenIddict,只將角色名稱存儲爲索賠。權限未被存儲,因爲如果更新了某些內容,則更改將立即在服務器上進行。我只需要檢查一次請求的權限,所以這對我來說很好atm – Gillardo 2016-02-22 20:12:57

回答

8

當您開始依賴注入時,Owin可能不是最開始的最友好的API。

我注意到了這部分代碼:

IUserService service = Startup.Container.GetInstance<IUserService>(); 

你可能會做這個作爲一種變通方法你瞭解如何使用構造函數之前。但我認爲那就是你的答案。 OAuthAuthorizationServerProvider是一個單例,所以你的IUserService也是單例,這個類的所有依賴也是單例。

您提到您在用戶服務中使用存儲庫。你可能不希望這個倉庫是單身,因爲我認爲這個倉庫將使用某種類型的DbContext。

因此,中間答案可能是您提出的解決方案。也許有一個更優雅的解決方案,如果你對UseOAuthAuthorizationServer方法做了些什麼研究。 Katana的源代碼可以在這裏找到:Katana source code

對於其他asp.net標識類的註冊,DSR評論中的鏈接將給你一個很好的起點。

+0

然後我想我誤解了以前的答案。我以前提出這個問題http://stackoverflow.com/questions/25997592/dependency-injection-using-simpleinjector-and-oauthauthorizationserverprovider,我想上面的代碼不是答案?正如我所說我是新的,所以不知道我明白你的答案?你能多解釋一下嗎? – Gillardo 2014-09-23 19:29:29

+0

確定鏈接是正確的?它鏈接到這個職位!我從你的問題中複製的代碼是很好的中間解決方案,因爲每次調用'GrantResourceOwnerCredentials'方法時都要求容器提供一個新實例。這是一件好事,因爲否則在應用程序的整個生命週期中,您的用戶服務將只有一個實例。 – 2014-09-23 19:38:06

+0

對不起,我有2個帖子,我已經在stackoverflow之間感到困惑。我以爲你在這裏回覆了另一個帖子http://stackoverflow.com/questions/26002866/unable-to-register-api-controller-using-simple-injector我實際上在另一個帖子上更改了這段代碼 – Gillardo 2014-09-23 19:48:05

23

我花了一些時間,以找出是否有可能在直接使用app.Use()方法Owin pipeling註冊OAuthAuthorizationServerOptions,而不是app.UseOAuthAuthorizationServer()這是剛剛超過app.Use()的擴展方法。 app.Use()有一個超負荷的地方,您可以註冊一個代表,您可以使用它來構建OAuthAuthorizationServerOptions

不幸的是,這項工作碰到了一個死衚衕,因爲即使我們使用委託進行構建,這很可能只會被Owin流水線調用一次,導致相同的結果,即單例OAuthAuthorizationServerOptions的實例,因此該類的所有依賴關係也將是單例。

所以唯一的解決方案就是在每次調用GrantResourceOwnerCredentials()方法時拉動您的UserService的新實例。

但是要遵循Simple Injector design principles這將是一個糟糕的設計,以保持依賴ApiAuthorizationServerProvider類中的容器,就像原始代碼所示。

更好的方法是使用UserService類的工廠,而不是直接將其從容器中拉出。下一個代碼顯示瞭如何執行此操作的示例:

首先,清除global.asax文件中的Application_Start()方法,並將所有啓動代碼放在Owin Startup()方法中。該Startup()方法的代碼:

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     var container = SimpleInjectorConfig.Register(); 

     GlobalConfiguration.Configure(WebApiConfig.Register); 

     Func<IUserService> userServiceFactory =() => 
       container.GetInstance<IUserService>(); 

     var OAuthServerOptions = new OAuthAuthorizationServerOptions() 
     { 
      AllowInsecureHttp = true, 
      TokenEndpointPath = new PathString("/token"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
      Provider = new ApiAuthorizationServerProvider(userServiceFactory) 
     }; 

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

通知我怎樣由完全地構造簡單注射器容器返回到調用者,因此可以直接使用改變了SimpleInjectorConfig.Register()功能的簽名。

現在改變你的ApiAuthorizationServerProvider類的構造函數,所以工廠方法可以注射:

public class ApiAuthorizationServerProvider : OAuthAuthorizationServerProvider 
{ 
    private Func<IUserService> userServiceFactory; 

    public ApiAuthorizationServerProvider(Func<IUserService> userServiceFactory) 
    { 
     this.userServiceFactory = userServiceFactory; 
    } 

    // other code deleted for brevity... 

    private IUserService userService 
    { 
     get 
     { 
      return this.userServiceFactory.Invoke(); 
     } 
    } 

    public override async Task GrantResourceOwnerCredentials(
     OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     // other code deleted for brevity... 
     // Just use the service like this 
     User user = this.userService.Query(e => e.Email.Equals(context.UserName) && 
      e.Password.Equals(context.Password)).FirstOrDefault(); 

     // other code deleted for brevity... 
    } 
} 

這樣你得到一個新的UserService每次的GrantResourceOwnerCredentials()方法被調用和UserService類背後的完整的依賴關係圖將遵循您在Simple Injector配置中定義的生命週期,而只依賴於應用程序組合根中的容器。

+0

@ user2736022您是否進一步解決了遇到的問題?我的答案是否有幫助?如果是這樣,不要忘記標記答案爲'已回答'!感謝名單! – 2014-10-06 12:43:58

+0

我在哪裏可以得到這個? 'SimpleInjectorConfig.Register();'或者設置這個 – Sherlock 2016-04-04 10:25:09

+0

@katana Simpleinjectorconfig是OP自己創建的一個類。這是他的作文根.... – 2016-04-04 20:06:35

7

首先,這是一個遲到的回答。我只是寫了這個以防別人遇到類似的問題,並以某種方式在未來鏈接到此頁面(像我一樣)。

以前的回答是合理的,但如果服務實際上是根據Web API請求註冊的,那麼問題就無法解決,我相信這是人們通常會使用依賴注入來處理像UserManager這樣的身份框架對象。

問題是當GrantResourceOwnerCredentials被調用時(通常當人們點擊'token'端點時),簡單的注入器將不會啓動api請求生命週期。要解決這個問題,你只需要啓動一個。

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     //...... 
     using (Startup.Container.BeginExecutionContextScope()) 
     { 
      var userService= Startup.Container.GetInstance<IUserService>(); 
      // do your things with userService.. 
     } 
     //..... 
    } 

使用BeginExecutionContextScope,簡單的注入器將啓動一個新的上下文作用域。但是,請記住它需要明確處理。

+0

這是一個不錯的解決方案。也適用於LightInject。 – 2015-06-22 13:53:14

+1

這增加了對IoC框架的依賴性。 @Ric。網隊的答案試圖隱藏這種依賴性,但並沒有解決執行上下文範圍問題。如何解決這兩個問題? – mcanti 2017-01-15 14:55:28

+0

@mcanti理論上我相信你可以爲token端點添加中間件來強制API請求生命週期的開始,我沒有自己嘗試它,不確定它是否會起作用。 – user3682091 2017-01-15 23:20:17

0

只要您註冊您的WebAPI的依賴解析器在App_Start SimpleInjectorConfig.Register();

喜歡這個

GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container); 

如果您使用的是推薦AsyncScopedLifestyle 然後你可以使用依賴解析器得到你的服務這樣的新實例

using (var scope = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.BeginScope()) 
{ 
    var _userService = scope.GetService(typeof(IUserService)) as IUserService; 
    //your code to use the service 
}