首页 > 解决方案 > 使用 AutoFac 将 BL 注入 OAuth

问题描述

我已按照此博客在我的 WebApi 中生成刷新令牌。在 RefreshTokenProvider 类中,他正在访问 AuthenticationRepository 以在表中为生成的刷新令牌创建一行。每件事都完美无缺。但现在我想将依赖注入添加到我的 WebAPI 项目中。对于依赖注入,我使用具有以下配置的 AutoFac

public class IocConfig
{
    public static AutofacWebApiDependencyResolver Configure()
    {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        builder.RegisterType<DefaultPrincipalProvider>().As<IPrincipalProvider>();
        builder.RegisterType<Logic>().AsSelf().InstancePerRequest();
        builder.RegisterType<DataAccess>().AsSelf().InstancePerRequest();
        builder.RegisterType<AuthRefreshTokenProvider>().As<IAuthenticationTokenProvider>().SingleInstance();
        var container = builder.Build();
        var resolver = new AutofacWebApiDependencyResolver(container);
        GlobalConfiguration.Configuration.DependencyResolver = resolver;
        return resolver;
    }
}

全球.asax.cs

protected void Application_Start()
{
    IocConfig.Configure();
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

控制器

public class ItemController : ApiController
{
    private Logic _logic;
    public ItemController(Logic logic)
    {
        _logic = logic;
    }
}

商业逻辑

public partial class Logic
{
    IPrincipalProvider _principalProvider;
    DataAccess _dataAccess;
    string _currentUserId;

    public Logic(DataAccess dataAccess,IPrincipalProvider provider)
    {
        _dataAccess = dataAccess;
    }

}

数据访问

public partial class DataAccess
{

    public DataAccess()
    {
    }

}

上述配置一切正常,但我坚持如何配置OAuthAuthorizationServerOptions。我需要将BusinessLogic实例传递给 AuthRefreshTokenProvider。

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    RefreshTokenProvider = new AuthRefreshTokenProvider(),// Need To use Dependency Injection Here
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true
};

AuthRefreshTokenProvider

public class AuthRefreshTokenProvider : IAuthenticationTokenProvider
{
    private Logic _logic;
    public AuthRefreshTokenProvider(Logic logic)
    {
        _logic = logic;
    }
}

我已经尝试了很多方法来完成这项工作。我知道问题出InstancePerRequest()在 SingleInstance 内部的实例化中。我一直在阅读很多文章和 stackoverflow 帖子以使其正常工作,但似乎无法使其正常工作

标签: c#asp.net-web-apidependency-injectionowinautofac

解决方案


如果我正确理解您的问题,您的问题是关于从单例服务上下文访问范围服务?

假设您的应用程序是 ASP.Net WebAPI(不是 Core),您需要捕获HttpRequestMessage才能访问请求范围。

首先,您需要在某个地方保存请求消息,并且因为您想从单例服务访问请求消息,所以新服务也需要是单例服务:

interface IHttpRequestAccessor
{
    HttpRequestMessage Request { get; set; }
}

class HttpRequestAccessor : IHttpRequestAccessor
{
    private readonly AsyncLocal<HttpRequestMessageHolder> _currentRequest =
        new AsyncLocal<HttpRequestMessageHolder>();

    public HttpRequestMessage Request
    {
        get => _currentRequest.Value?.Value;
        set
        {
            HttpRequestMessageHolder holder = _currentRequest.Value;
            if (holder != null)
            {
                holder.Value = null;
            }

            if (value != null)
            {
                _currentRequest.Value = new HttpRequestMessageHolder { Value = value };
            }
        }
    }

    private class HttpRequestMessageHolder
    {
        public HttpRequestMessage Value { get; set; }
    }
}

builder.RegisterType<HttpRequestAccessor>()
    .As<IHttpRequestAccessor>()
    .SingleInstance();

这是如何工作的?

此类公开一个属性以获取和设置HttpRequestMessage当前异步执行上下文。因为每个请求的数据上下文是不同的,并且在 WebAPI 请求管道的所有步骤中都被继承,所以我们可以将它设置在一个地方,然后在另一个地方为同一个 HTTP 请求使用它。这与IHttpContextAccessorASP.Net Core 应用程序中的概念相同。

现在我们需要在某个地方进行捕获和设置IHttpRequestAccessor.Request委托处理程序是一个不错的选择:

class CaptureHttpRequestMessageHandler : DelegatingHandler
{
    private readonly IHttpRequestAccessor _requestMessageAccessor;

    public CaptureHttpRequestMessageHandler(IHttpRequestAccessor requestMessageAccessor)
    {
        _requestMessageAccessor = requestMessageAccessor;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (_requestMessageAccessor != null)
        {
            _requestMessageAccessor.Request = request;
        }

        return base.SendAsync(request, cancellationToken);
    }
}

并将其添加到配置中:

GlobalConfiguration.Configuration.MessageHandlers.Add(
    new CaptureHttpRequestMessageHandler(/* resolve services here */));

毕竟,注入IHttpRequestAccessor您的AuthRefreshTokenProvider并删除Logic.

每当Logic需要时,使用以下方法即时解决它:

_httpRequestAccessor.Request.GetDependencyScope().GetService(typeof(Logic));

只要在HTTP 请求AuthRefreshTokenProvider时调用,这应该可以工作。


推荐阅读