我正在创建一个Web Api应用程序,我想使用承载令牌进行用户身份验证。 我在this post之后实现了令牌逻辑,一切似乎都运行正常。 注意:我没有使用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'从请求实例的范围中可见。这通常表示SingleInstance()组件(或类似场景)正在请求注册为每HTTP请求的组件。在Web集成下,始终从DependencyResolver.Current或ILifetimeScopeProvider.RequestLifetime请求依赖项,从不从容器本身请求
有没有办法在这个类中使用依赖注入?我使用存储库模式来访问我的实体,所以我不认为创建对象上下文的新实例是个好主意。这样做的正确方法是什么?
答案 0 :(得分:14)
我遇到了类似的问题。
此处的问题是,当您尝试在提供程序中注入IUserService
时,Autofac会检测到它已注册为InstancePerRequest
(使用众所周知的生命周期范围标记'AutofacWebRequest'
)SimpleAuthorizationServerProvider
已在'root'
容器范围内注册,'AutofacWebRequest'
范围不可见。
建议的解决方案是将依赖关系注册为InstancePerLifetimeScope
。这显然解决了问题,但引入了新的问题。所有依赖项都在'root'
范围内注册,这意味着对所有请求具有相同的DbContext
和服务实例。 Steven在此answer中解释得非常好。为什么在请求之间共享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>();
...
}
我在OAuthAuthorizationServerProvider
方法中ConfigureOAuth
注册和注入的方式与Laurentiu Stamate another response在此问题中提出的方式类似,{{1 }}。
我以同样的方式实施了SingleInstance()
。
修改强>
@BramVandenbussche,这是RefreshTokenProvider
类中的Configuration
方法,您可以在其中看到添加到OWIN管道的中间件的顺序:
Startup
答案 1 :(得分:11)
要在SimpleAuthorizationServerProvider
中使用依赖项注入,您必须像其他任何类型一样将IOAuthAuthorizationServerProvider
注册到Autofac容器。你可以这样做:
builder
.RegisterType<SimpleAuthorizationServerProvider>()
.As<IOAuthAuthorizationServerProvider>()
.PropertiesAutowired() // to automatically resolve IUserService
.SingleInstance(); // you only need one instance of this provider
您还需要将容器传递给ConfigureOAuth
方法,让Autofac像这样解析您的实例:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = container.Resolve<IOAuthAuthorizationServerProvider>()
};
如果对象中的属性不通过外部数据更改,则应始终使用单个实例(假设您拥有在控制器中设置的属性,该属性依赖于存储在数据库中的某些信息 - 在这种情况下,您应该使用InstancePerRequest)。
答案 2 :(得分:1)
我还尝试使用OwinContextExtensions.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);
}