Dependency injection OAuthAuthorizationServerProvider实现中的Autofac依赖项注入
我正在创建一个Web Api应用程序,我想使用承载令牌进行用户身份验证。 我实现了令牌逻辑,如下所示,一切似乎都很好。 注意:我没有使用ASP.NET身份提供程序。相反,我为它创建了一个自定义用户实体和服务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
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);
}