OAuthAuthorizationServerProvider实现中的Autofac依赖注入

Iva*_*nov 20 dependency-injection autofac owin asp.net-web-api2 bearer-token

我正在创建一个Web Api应用程序,我想使用承载令牌进行用户身份验证.我实现了令牌逻辑,按照这篇文章,一切似乎都运行正常.注意:我没有使用ASP.NET身份提供程序.相反,我为它创建了一个自定义用户实体和服务.

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

    }
}
Run Code Online (Sandbox Code Playgroud)

这是我对SimpleAuthorizationServerProvider类的实现

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

    }
}
Run Code Online (Sandbox Code Playgroud)

我调用/ token url后,收到以下错误

从请求实例的作用域中看不到具有匹配"AutofacWebRequest"的标记的作用域.这通常表示SingleInstance()组件(或类似场景)正在请求注册为每HTTP请求的组件.在Web集成下,始终从DependencyResolver.Current或ILifetimeScopeProvider.RequestLifetime请求依赖项,从不从容器本身请求

有没有办法在这个类中使用依赖注入?我使用存储库模式来访问我的实体,所以我认为创建对象上下文的新实例并不是一个好主意.这样做的正确方法是什么?

jum*_*uro 16

我遇到过类似的问题.

这里的问题是,当您尝试IUserService在提供程序中注入时,Autofac会检测到它已注册为InstancePerRequest(使用着名的生命周期范围标记'AutofacWebRequest'),但是SimpleAuthorizationServerProvider在'root'容器范围内注册了'AutofacWebRequest'范围不可见的范围.

建议的解决方案是将依赖性注册为InstancePerLifetimeScope.这显然解决了问题,但引入了新的问题.所有依赖项都在'root'范围中注册,这意味着DbContext为所有请求提供相同的服务实例.史蒂文在这个答案中解释得非常好,为什么分享DbContext两个请求之间不是一个好主意.

经过顺藤摸瓜的任务,我已经解决得到的问题'AutofacWebRequest'来自OwinContext于OAuthAuthorizationServerProvider类,并从中解决,而不是让Autofac自动注入他们的服务依存关系.为此,我使用了OwinContextExtensions.GetAutofacLifetimeScope()扩展方法Autofac.Integration.Owin,参见下面的示例:

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

我OAuthAuthorizationServerProvider在ConfigureOAuth方法中注册和注入的方式与Laurentiu Stamate在另一个对此问题的回答中提出的方式相似,如同SingleInstance().我RefreshTokenProvider以同样的方式实现了.

编辑

@BramVandenbussche,这是我Configuration在Startup类中的方法,在那里你可以看到添加到OWIN管道的中间件的顺序:

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

  • 谢谢你的回复.我确实执行了对'app.UseAutofacMiddleware(容器)`的调用,但你链接我的文章也帮助我弄清楚我把它称为太晚了.我必须在设置OAuth之前移动该行并修复该问题.我现在坚持使用`一个注册创建'System.Security.Principal.IPrincipal'实例的委托返回null`异常,但这是因为在`/ token`调用的情况下没有CurrentContext. (3认同)
  • @BramVandenbussche,在`Startup`类中查看我的`Configuration()`方法,在那里你可以看到添加到OWIN管道的中间件的顺序.由于我无法在评论中添加格式代码,因此我编辑了我的答案. (2认同)

Lau*_*ate 11

要使用依赖注入,SimpleAuthorizationServerProvider您必须IOAuthAuthorizationServerProvider像任何其他类型一样注册到Autofac容器.你可以这样做:

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

您还需要将容器传递给ConfigureOAuth方法,并让Autofac解析您的实例,如下所示:

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

如果对象中的属性不通过外部数据更改,则应始终使用单个实例(假设您拥有在控制器中设置的属性,该属性依赖于存储在数据库中的某些信息 - 在这种情况下,您应使用InstancePerRequest) .