Введите BL в OAuth с помощью AutoFac - PullRequest
1 голос
/ 02 апреля 2020

Я следовал за этим Блогом , чтобы сгенерировать Refre sh токен в моем WebApi. В классе RefreshTokenProvider он обращается к AuthenticationRepository, чтобы создать строку в таблице для сгенерированного токена refre sh. Все работает отлично. Но теперь я хочу добавить инъекцию зависимостей в мой проект WebAPI. Для внедрения зависимостей я использую AutoFa c со следующими конфигурациями

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;
    }
}

Global.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;
    }
}

Business Logi c

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

    public Logic(DataAccess dataAccess,IPrincipalProvider provider)
    {
        _dataAccess = 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, чтобы заставить это работать, но не могу заставить его работать

1 Ответ

1 голос
/ 02 апреля 2020

Если я правильно понимаю ваш вопрос, ваш вопрос касается доступа к сервисам с заданной областью из контекста одноэлементного сервиса?

Предполагается, что ваше приложение ASP. Net WebAPI (не Core), вам нужно перехватить HttpRequestMessage для того, чтобы иметь доступ к области запроса.

Во-первых, вам нужно где-то хранить сообщение запроса, и, поскольку вы хотите получить доступ к сообщению запроса из службы singleton, новая служба также должна быть singleton сервисом:

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 для текущий асин c контекст выполнения. Поскольку контекст данных различен для каждого запроса и наследуется на всех этапах конвейера запросов WebAPI, мы могли бы установить его в одном месте, а затем использовать в другом месте для того же HTTP-запроса. Это та же концепция, что и IHttpContextAccessor в ASP. Net Базовых приложениях.

Теперь нам нужно где-то захватить и установить 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));

Пока вызывается AuthRefreshTokenProvider, когда существует HTTP-запрос, это должно работать.

...