依赖注入(使用SimpleInjector)和OAuthAuthorizationServerProvider

Gil*_*rdo 17 c# asp.net asp.net-mvc dependency-injection simple-injector

新的依赖注入,所以这可能是一个简单的问题,但我已经尝试过,无法弄清楚,我正在使用Simple Injector.

我有一个完全使用SimpleInjector的WebApi,现在我想使用OAuth实现安全性.

为此,我开始学习本教程,这非常有用,但不使用Dependancy Injection

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);
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个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());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我在上面评论过,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);

    }
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能使用依赖注入?这必须发生很多,必须能够做一些事情来处理它.我确信它很简单,但我还在学习.

Ric*_*Net 24

我花了一些时间来确定是否可以直接OAuthAuthorizationServerOptions使用该app.Use()方法在Owin管道中注册,而不是app.UseOAuthAuthorizationServer()仅仅是扩展方法app.Use().app.Use()有一个重载,你可以注册一个你可以用来构建的委托OAuthAuthorizationServerOptions.

不幸的是这方面的努力进入了死胡同,因为它似乎是,即使我们会使用一个委托的建设,这将是最有可能只由Owin管道,导致同样的结果,即单一实例调用一次的OAuthAuthorizationServerOptions和因此,这个类的所有依赖关系也将是单例.

因此,保持工作正常运行的唯一解决方案是UserService每次GrantResourceOwnerCredentials()调用方法时拉出一个新的实例.

但是,遵循Simple Injector设计原则,在ApiAuthorizationServerProvider类中保持对容器的依赖是不好的设计,就像原始代码所示.

更好的方法是为类使用工厂,UserService而不是直接从容器中拉出它.下一个代码显示了如何执行此操作的示例:

首先,清除Application_Start()global.asax文件中的方法,并将所有启动代码放在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());
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意我是如何SimpleInjectorConfig.Register()通过将完全配置的Simple Injector容器返回给调用者来更改函数的签名,以便可以直接使用它.

现在更改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...
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,UserService每次GrantResourceOwnerCredentials()调用该方法时都会获得一个新的,并且UserService类后面的完整依赖关系图将遵循您在Simple Injector配置中定义的生命周期,而您仅依赖于应用程序的组合根中的容器.


Ric*_*Net 8

当您从依赖注入开始时,Owin可能不是最友好的API.

我在你的代码中注意到了这一部分:

IUserService service = Startup.Container.GetInstance<IUserService>();
Run Code Online (Sandbox Code Playgroud)

在找到如何使用构造函数之前,您可能正在将此作为一种解决方法.但我认为那是你的答案.OAuthAuthorizationServerProvider是一个单例,因此您的IUserService也将是一个单例,并且此类的所有依赖项也将是单例.

您提到您在用户服务中使用存储库.你可能不希望这个存储库是单例,因为我想这个存储库将使用某种类型的DbContext.

所以中间答案可能就是你已经做出的解决方案.如果您对UseOAuthAuthorizationServer方法的确切做法进行一些研究,也许有更优雅的解决方案.Katana的源代码可以在这里找到:Katana源代码

对于其他asp.net身份类的注册,DSR注释中的链接将为您提供一个良好的起点.


use*_*091 7

首先,这是一个迟到的答案.我刚刚写下来,以防其他人遇到类似的问题,并以某种方式将来链接到这个页面(像我一样).

前面的答案是合理的,但是如果服务实际上是按照Web API请求注册的话,那么就不能解决问题,我相信,如果他们想对UserManager这样的身份框架对象使用依赖注入,人们通常会这样做.

问题是当调用GrantResourceOwnerCredentials时(通常当人们点击"令牌"端点时),简单的注入器将不会启动api请求生命周期.要解决这个问题,您需要做的只是启动一个.

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

使用BeginExecutionContextScope,简单的注入器将启动一个新的上下文范围.但是,请记住它需要明确处理.