C# 依赖项注入(使用SimpleInjector)和OAuthAuthorizationServerProvider
对于依赖注入来说是新的,所以这可能是一个简单的问题,但我已经尝试过了,但无法解决它,我使用的是SimpleInjector 我有一个使用SimpleInjector的WebApi,现在我想使用OAuth实现安全性 为了做到这一点,我开始学习本教程,它非常有用,但不使用依赖注入 我的global.asax文件如下所示,用于设置依赖注入(工作正常) 我已经创建了一个Startup.Auth.cs文件来配置OAuthC# 依赖项注入(使用SimpleInjector)和OAuthAuthorizationServerProvider,c#,asp.net,asp.net-mvc,dependency-injection,simple-injector,C#,Asp.net,Asp.net Mvc,Dependency Injection,Simple Injector,对于依赖注入来说是新的,所以这可能是一个简单的问题,但我已经尝试过了,但无法解决它,我使用的是SimpleInjector 我有一个使用SimpleInjector的WebApi,现在我想使用OAuth实现安全性 为了做到这一点,我开始学习本教程,它非常有用,但不使用依赖注入 我的global.asax文件如下所示,用于设置依赖注入(工作正常) 我已经创建了一个Startup.Auth.cs文件来配置OAuth public class Startup { public void Co
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);
}
}
公共类ApitAuthorizationServerProvider:OAuthAuthorizationServerProvider
{
私人Iuser服务(u服务);;
公共ApiAuthorizationServerProvider(IUserService服务)
{
_服务=服务;
}
公共覆盖异步任务ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext(上下文)
{
context.Validated();
}
公共重写异步任务GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext(上下文)
{
context.OwinContext.Response.Headers.Add(“访问控制允许来源”,
新[]{“*”});
IUserService=Startup.Container.GetInstance();
User User=\u service.Query(e=>e.Email.Equals(context.UserName)&&
e、 Password.Equals(context.Password)).FirstOrDefault();
if(user==null)
{
context.SetError(“无效的授权”,
“用户名或密码不正确。”);
返回;
}
var identity=newclaimsidentity(context.Options.AuthenticationType);
identity.AddClaim(新声明(“sub”,context.UserName));
identity.AddClaim(新声明(“角色”、“用户”));
上下文验证(身份);
}
}
如何使用依赖项注入实现这一点?这种情况必须经常发生,并且必须能够采取措施来应对。我相信这很简单,但我仍在学习。从依赖注入开始,Owin可能不是最友好的API 我注意到您代码中的这一部分:
IUserService service = Startup.Container.GetInstance<IUserService>();
IUserService service=Startup.Container.GetInstance();
在了解如何使用构造函数之前,您可能会将此作为一种变通方法。但我想这就是你的答案。OAuthAuthorizationServerProvider是单例的,因此您的IUserService也将是单例的,并且该类的所有依赖项也将是单例的
您提到在用户服务中使用存储库。您可能不希望该存储库是单例的,因为我认为该存储库将使用某种DbContext
因此,中间答案可能是您已经提出的解决方案。如果您研究一下UseAuthAuthorizationServer方法的确切功能,也许会有一个更优雅的解决方案。Katana的源代码可以在这里找到:
对于其他asp.net标识类的注册,DSR注释中的链接将为您提供一个良好的起点。我花了一些时间来了解是否可以使用
app.Use()
方法在Owin管道中注册OAuthorizationServerOptions
,而不是app.useAuthorizationServer()
,后者只是app.Use()
的扩展方法app.Use()
有一个重载,您可以在其中注册一个委托,该委托可用于构造OAuthAuthorizationServerOptions
不幸的是,这项工作遇到了死胡同,因为即使我们使用委托进行构建,Owin管道也很可能只调用一次,从而产生相同的结果,即,OAuthAuthorizationServerOptions
的一个单例实例,因此该类的所有依赖项也将是单例的
因此,保持一切正常运行的唯一解决方案是,每次调用GrantResourceOwnerCredentials()
方法时,都要拉取UserService
的一个新实例
但是,按照原始代码所示,在apiaauthorizationserverprovider
类中保持对容器的依赖性将是一种糟糕的设计
更好的方法是为UserService
类使用工厂,而不是直接从容器中提取它。下一段代码显示了如何执行此操作的示例:
首先,清除global.asax文件中的Application\u Start()
方法,并将所有启动代码放在Owinstartup()
方法中。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());
}
}
这样,每次调用
GrantResourceOwnerCredentials()
方法时,您都会得到一个新的UserService
,并且UserService
类后面的完整依赖关系图将遵循您在简单喷油器配置中定义的生命周期,虽然您仅在应用程序的合成根目录中依赖容器。首先,这是一个迟来的答案。我只是写下来,以防其他人遇到类似的问题,并以某种方式得到链接到这个页面(像我一样)在未来
前面的答案是合理的,但如果服务实际上是按照Web API请求注册的,则无法解决问题。我相信,如果人们希望对UserManager之类的identity framework对象使用依赖项注入,通常会这样做
问题是当GrantResourceOwnerCredentials被调用时(通常是当人们到达“令牌”端点时),simple injector不会启动api请求
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());
}
}
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...
}
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//......
using (Startup.Container.BeginExecutionContextScope())
{
var userService= Startup.Container.GetInstance<IUserService>();
// do your things with userService..
}
//.....
}
GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
using (var scope = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.BeginScope())
{
var _userService = scope.GetService(typeof(IUserService)) as IUserService;
//your code to use the service
}