Dependency injection OAuthAuthorizationServerProvider实现中的Autofac依赖项注入

Dependency injection OAuthAuthorizationServerProvider实现中的Autofac依赖项注入,dependency-injection,autofac,owin,asp.net-web-api2,bearer-token,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 HttpConfigurat

我正在创建一个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());

    }
}
这是我对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);

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

从请求实例的作用域中看不到标记与“AutofacWebRequest”匹配的作用域。这通常表示根据HTTP请求注册的组件正被web集成下的SingleInstance()组件(或类似场景)请求。始终从DependencyResolver.Current或ILifetimeScopeProvider.RequestLifetime请求依赖项,而不是从容器本身


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

要在
SimpleAuthorizationServerProvider
中使用依赖项注入,您必须像其他类型一样将
IOAuthorizationServerProvider
注册到Autofac容器中。您可以这样做:

builder
  .RegisterType<SimpleAuthorizationServerProvider>()
  .As<IOAuthAuthorizationServerProvider>()
  .PropertiesAutowired() // to automatically resolve IUserService
  .SingleInstance(); // you only need one instance of this provider
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
    AllowInsecureHttp = true,
    TokenEndpointPath = new PathString("/token"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
    Provider = container.Resolve<IOAuthAuthorizationServerProvider>()
};

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

我也遇到了类似的问题

这里的问题是,当您尝试在提供程序中注入
IUserService
时,Autofac检测到它已注册为
InstancePerRequest
(使用众所周知的生存期范围标记
“AutofacWebRequest”
)但是,
SimpleAuthorizationServerProvider
'root'
容器范围中注册,其中
'AutofacWebRequest'
范围不可见

建议的解决方案是将依赖项注册为
InstancePerLifetimeScope
。这显然解决了问题,但引入了新的问题。所有依赖项都注册在
'root'
范围内,这意味着所有请求都具有相同的
DbContext
和服务实例。这很好地解释了为什么在请求之间共享
DbContext
不是一个好主意


在深入调查任务之后,我解决了从
OAuthAuthorizationServerProvider
类中的
OwinContext
获取
'AutofacWebRequest'
并从中解析服务依赖项的问题,而不是让Autofac自动注入它们。为此,我使用了
Autofac.Integration.Owin
中的
owincontextensions.GetAutofacLifetimeScope()
扩展方法,请参见下面的示例:

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

我还尝试使用owincontextensions.GetAutofacLifetimeScope来回答@jumuro问题,这节省了我的时间。这个答案没有在运行时注册IUserService,而是提供了一个在请求后验证/创建实例服务的选项

我添加了一些新的答案,因为我的名声不好,我还不能发表评论,但我添加了额外的指导代码来帮助别人

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

    }
public override异步任务GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext)
{
尝试
{
if(服务==null)
{

var scope=Autofac.Integration.Owin.owincontextensions.GetAutofacLifetimeScope(context.OwinContext); service=scope.Resolve(); } var user=await service.FindUserAsync(context.UserName); if(user?.HashedPassword!=Helpers.CustomPasswordHasher.GetHashedPassword(context.Password,user?.Salt)) { SetError(“无效的授权”,“用户名或密码不正确”); 返回; } } 捕获(例外情况除外) { SetError(“无效的授权”,例如Message); 返回; } var identity=newclaimsidentity(context.Options.AuthenticationType); AddClaim(新声明(ClaimTypes.Name,context.UserName)); AuthenticationProperties=CreateProperties(context.UserName); AuthenticationTicket=新的AuthenticationTicket(标识、属性); 上下文。已验证(票证); context.Request.context.Authentication.sign(标识); }
您是否找到了解决方案?我也有同样的问题,无法……谢谢。@shenku:我已经添加了一个对我有用的回复。我希望这会有所帮助。owincontextensions.GetAutofacLifetimeScope为我返回null,您还有其他设置吗?@BramVandenbussche,在调用此方法的更改之前,您的项目是否正在工作?@BramVandenbussche,您正在调用
app.UseAutofacMiddleware(container)
?正如Alex Meyer Gleaves在他的一篇文章中所指出的:“这是必需的,因为除了启用中间件DI支持外,这还负责将生存期范围放置在OWIN上下文中”。请稍后回复。我确实调用了
app.UseAutofacMiddleware(container)
,但是你链接到我的那篇文章也帮助我发现我调用它太晚了。在安装OAuth之前,我必须移动这条线,这就解决了这个问题。我现在被一个注册来创建'System.Security.Principal.IPrincipal'实例的委托所困扰,该委托返回null异常,但这是因为在调用
/token
时没有CurrentContext。@BramVandenbussche,请参阅
启动
类中的我的
配置()
方法,w
    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);

    }