OAuthAuthorizationServerProvider 实现中的 Autofac 依赖注入
Posted
技术标签:
【中文标题】OAuthAuthorizationServerProvider 实现中的 Autofac 依赖注入【英文标题】:Autofac dependency injection in implementation of OAuthAuthorizationServerProvider 【发布时间】:2014-11-10 08:35:07 【问题描述】:我正在创建一个 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”匹配的标记的范围。这通常表明注册为 per-HTTP 请求的组件正在由 SingleInstance() 组件(或类似场景)请求。在 Web 集成下,始终从 DependencyResolver.Current 或 ILifetimeScopeProvider.RequestLifetime 请求依赖项,而不是从容器本身
有没有办法在这个类中使用依赖注入?我正在使用存储库模式来访问我的实体,因此我认为创建对象上下文的新实例不是一个好主意。这样做的正确方法是什么?
【问题讨论】:
您找到解决方案了吗?我有同样的问题,无法...谢谢。 @shenku:我添加了对我有用的回复。我希望它有所帮助。 【参考方案1】:要在 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)。
【讨论】:
那么你可以使用private readonly IAuthService _authService; public SimpleAuthorizationServerProvider(IAuthService authService) _authService = authService;
@ShalomDahan 看看这个页面:autofaccn.readthedocs.io/en/latest/resolve【参考方案2】:
我也遇到过类似的问题。
这里的问题是,当您尝试在提供程序中注入 IUserService
时,Autofac 检测到它已注册为 InstancePerRequest
(使用众所周知的生命周期范围标记 'AutofacWebRequest'
)但 SimpleAuthorizationServerProvider
在'AutofacWebRequest'
范围不可见的'root'
容器范围中注册。
建议的解决方案是将依赖项注册为InstancePerLifetimeScope
。这显然解决了问题,但引入了新问题。所有依赖项都在'root'
范围内注册,这意味着所有请求都具有相同的DbContext
和服务实例。 Steven 在这个 answer 中很好地解释了为什么在请求之间共享 DbContext
不是一个好主意。
经过更深入的调查任务后,我已经解决了从 OAuthAuthorizationServerProvider
类中的 OwinContext
获取 'AutofacWebRequest'
并从中解决服务依赖关系的问题,而不是让 Autofac 自动注入它们。为此,我使用了来自Autofac.Integration.Owin
的OwinContextExtensions.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>();
...
我在ConfigureOAuth
方法中进行了OAuthAuthorizationServerProvider
注册和注入,其方式与Laurentiu Stamate 在another response 中针对此问题提出的方法类似,如SingleInstance()
。
我以同样的方式实现了RefreshTokenProvider
。
编辑
@BramVandenbussche,这是我在Startup
类中的Configuration
方法,您可以在其中看到添加到OWIN 管道的中间件的顺序:
public void Configuration(IAppBuilder app)
// Configure Autofac
var container = ConfigureAutofac(app);
// Configure CORS
ConfigureCors(app);
// Configure Auth
ConfigureAuth(app, container);
// Configure Web Api
ConfigureWebApi(app, container);
【讨论】:
OwinContextExtensions.GetAutofacLifetimeScope 为我返回 null,您还有其他设置吗? @BramVandenbussche,您的项目在更改调用此方法之前是否正常工作? @BramVandenbussche,你打电话给app.UseAutofacMiddleware(container)
吗?正如 Alex Meyer-Gleaves 在他的 posts 之一中指出的那样:“这是必需的,因为除了启用中间件 DI 支持外,这还负责将生命周期范围置于 OWIN 上下文中”。
感谢您的回复。我确实拨打了app.UseAutofacMiddleware(container)
,但你链接我的文章也帮助我弄清楚我打电话太晚了。我必须在设置 OAuth 之前移动该行并解决该问题。我现在遇到了 A delegate registered to create instances of 'System.Security.Principal.IPrincipal' returned null
异常,但这是因为在 /token
调用的情况下没有 CurrentContext。
@BramVandenbussche,查看我在Startup
类中的Configuration()
方法,您可以在其中查看添加到OWIN 管道的中间件的顺序。由于我无法在评论中添加格式代码,我编辑了我的答案。【参考方案3】:
我还使用 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);
【讨论】:
以上是关于OAuthAuthorizationServerProvider 实现中的 Autofac 依赖注入的主要内容,如果未能解决你的问题,请参考以下文章