新接触依赖注入,所以这可能是一件简单的事情,但我已经尝试过了,但我无法弄清楚,我使用的是simple Injector。
我有一个使用SimpleInjector的WebApi,现在我想用OAuth实现安全性。
为此,我开始学习本教程,它非常有用,但没有使用依赖注入
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);
}
我已经创建了一个用于配置OAuth的Startup.Auth.cs文件
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);
}
}
我如何通过依赖注入来实现这一点呢?这必须经常发生,并且必须能够采取措施来处理它。我相信这很简单,但我还在学习。
发布于 2014-09-25 17:09:40
我花了一些时间来了解是否可以直接使用app.Use()
方法在Owin管道中注册OAuthAuthorizationServerOptions
,而不是使用app.UseOAuthAuthorizationServer()
,后者只是app.Use()
上的一个扩展方法。app.Use()
有一个重载,你可以在其中注册一个委托,你可以用它来构造OAuthAuthorizationServerOptions
。
不幸的是,这项工作走进了死胡同,因为似乎即使我们使用委托进行构造,这也很可能只会被Owin管道调用一次,从而导致相同的结果,即OAuthAuthorizationServerOptions
的单个实例,因此该类的所有依赖项也将是单个的。
因此,保持正常工作的唯一解决方案是在每次调用GrantResourceOwnerCredentials()
方法时拉出一个新的UserService
实例。
但是,为了遵循Simple Injector design principles,在ApiAuthorizationServerProvider
类中保持对容器的依赖将是一个糟糕的设计,如原始代码所示。
一种更好的方法是使用UserService
类的工厂,而不是直接从容器中提取它。下面的代码展示了如何做到这一点的示例:
首先,清除global.asax文件中的Application_Start()
方法,并将所有启动代码放入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());
}
}
请注意,我如何通过将完整配置的Simple Injector容器返回给调用者来更改SimpleInjectorConfig.Register()
函数的签名,以便可以直接使用它。
现在更改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...
}
}
这样,每次调用GrantResourceOwnerCredentials()
方法时,您都会获得一个新的UserService
,并且UserService
类背后的完整依赖关系图将遵循您在简单注入器配置中定义的生命周期,而您仅依赖于应用程序的组合根中的容器。
发布于 2014-09-24 03:17:54
当您开始使用依赖注入时,Owin可能不是最友好的API。
我注意到你代码中的这一部分:
IUserService service = Startup.Container.GetInstance<IUserService>();
在您了解如何使用构造函数之前,您可能会将此作为一种解决方法。但我想这就是你的答案。OAuthAuthorizationServerProvider是单例的,因此您的IUserService也将是单例的,这个类的所有依赖项也将是单例的。
您提到过您在用户服务中使用存储库。您可能不希望这个存储库是单例的,因为我认为这个存储库将使用某种类型的DbContext。
因此,中间答案可能是您已经做出的解决方案。如果您研究一下UseOAuthAuthorizationServer方法到底做了什么,也许会有一个更好的解决方案。片断的源代码可以在这里找到:Katana source code
对于其他asp.net身份类的注册,DSR注释中的链接将为您提供一个很好的起点。
发布于 2015-04-26 11:09:26
首先,这是一个迟来的答案。我只是写下来,以防其他人遇到类似的问题,并以某种方式链接到这个页面(像我)在未来。
前面的答案是合理的,但如果服务实际上是按Web API请求注册的,则不会解决问题,我相信这是人们在使用依赖项注入标识框架对象(如UserManager )时通常会做的事情。
问题是当GrantResourceOwnerCredentials被调用时(通常是当人们到达'token‘端点时),简单的注入器不会启动api请求生命周期。要解决这个问题,您只需启动一个。
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//......
using (Startup.Container.BeginExecutionContextScope())
{
var userService= Startup.Container.GetInstance<IUserService>();
// do your things with userService..
}
//.....
}
使用BeginExecutionContextScope,简单注入器将启动一个新的上下文作用域。但是,请记住,它需要显式处理。
https://stackoverflow.com/questions/25997592
复制相似问题