2

UPDATE: К сожалению, перезагрузка Windows, решить эту проблему -.-авторизации для анонимного пользователя (автоматическая аутентификация)


В нашем приложении ASP.NET ядра (1,0 RC2), мы имеем следующее требование : только пользователи из внутренней сети должны иметь доступ к некоторым страницам «Отладка» (размещается в MVC Core). Это общедоступный веб-сайт, и у нас нет пользовательских логинов, вместо этого мы до сих пор управляли им с помощью авторизации на основе IP-адреса (обратите внимание: это не является риском для безопасности в нашем случае, потому что у нас есть прокси между ними, поэтому IP-адрес нельзя подделать извне).

Мы также хотим реализовать такую ​​авторизацию на основе IP-адресов в ядре ASP.NET. Для этого мы используем настраиваемую политику "DebugPages" и соответствующие определения [Authorize(Policy="DebugPages")] на контроллере MVC. Потом мы заметили, что мы должны быть авторизованным пользователем, чтобы получить AuthorizeAttribute прыгать и мы создаем один в запросе трубопровода, который дает на следующий код в Startup.cs (укороченный для краткости):

public void ConfigureServices(IServiceCollection services) 
{ 
    ... 

    services.AddAuthorization(options => 
    { 
     options.AddPolicy(
      "DebugPages", 
      policy => policy.RequireAssertion(
       async context => await MyIPAuthorization.IsAuthorizedAsync())); 
    }); 
} 

public void Configure(IApplicationBuilder app) 
{ 
    ... 

    app.Use(async (context, next) => 
    { 
     context.User = new ClaimsPrincipal(new GenericIdentity("anonymous")); 
     await next.Invoke(); 
    }); 

    ... 
} 

Теперь это отлично работает при запуске в Debug от Visual Studio 2015 (с IIS Express). Но, к сожалению, это не работает при непосредственном запуске dotnet run (с Kestrel) из командной строки. В этом случае мы получаем следующее исключение:

InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic 

Той же ошибка возникает, когда мы обеспечиваем текущую ОС Windows принципал вместо доверителя с пользовательским анонимной идентичностью - так каждый раз, когда пользователь автоматический -ally проверки подлинности ...

Итак, почему существует разница между хостингом в IIS Express и Kestrel? Любые предложения по решению проблемы?

+0

Эта часть кода отлично работает с пустельгой (dotnet run) для меня. Кажется, причина исключения не связана с этим кодом. –

+0

Черт ... Я просто перезагрузился, и теперь все работает нормально. Я не знаю, что случилось ... старые добрые Windows, боюсь (если это не хорошо - перезагрузитесь!). – Matthias

+0

Привет. У меня такая же проблема. Не в Windows, а в докере тоже. Версия - Core 1.0. Я исследовал проблему и увидел, что context.Authentication.HttpAuthhenticationFeature.Handler имеет значение NULL, когда вы используете Kestrel, а не null при использовании IIS. Я предполагаю, что трюк находится в .UseIISIntegration() в Program.cs – aligin

ответ

1

Итак, после некоторых исследований, как я упомянул в комментариях, я обнаружил, что httpContext.Authentication.HttpAuthhenticationFeature.Handler является нулевым, когда вы запускаете приложение под «самоисследованной» пустельгой. Но когда вы используете IIS, Handler создавал экземпляр Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler. Эта конкретная реализация обработчика является частью .UseIISIntegration() в Program.cs.

Итак, я решил использовать часть этой реализации в своем приложении и обрабатывать неаутентифицированные запросы.

Для моей службы WebAPI (без каких-либо просмотров) я использую IdentityServer4.AccessTokenValidation, которая использует за кулисами OAuth2IntrospectionAuthentication и JwtBearerAuthentication.

Создать файлы

KestrelAuthenticationMiddleware.cs

public class KestrelAuthenticationMiddleware 
{ 
    private readonly RequestDelegate _next; 
    public KestrelAuthenticationMiddleware(RequestDelegate next) 
    { 
     _next = next; 
    } 
    public async Task Invoke(HttpContext context) 
    { 
     var existingPrincipal = context.Features.Get<IHttpAuthenticationFeature>()?.User; 
     var handler = new KestrelAuthHandler(context, existingPrincipal); 
     AttachAuthenticationHandler(handler); 
     try 
     { 
      await _next(context); 
     } 
     finally 
     { 
      DetachAuthenticationhandler(handler); 
     } 
    } 

    private void AttachAuthenticationHandler(KestrelAuthHandler handler) 
    { 
     var auth = handler.HttpContext.Features.Get<IHttpAuthenticationFeature>(); 
     if (auth == null) 
     { 
      auth = new HttpAuthenticationFeature(); 
      handler.HttpContext.Features.Set(auth); 
     } 
     handler.PriorHandler = auth.Handler; 
     auth.Handler = handler; 
    } 

    private void DetachAuthenticationhandler(KestrelAuthHandler handler) 
    { 
     var auth = handler.HttpContext.Features.Get<IHttpAuthenticationFeature>(); 
     if (auth != null) 
     { 
      auth.Handler = handler.PriorHandler; 
     } 
    } 
} 

KestrelAuthHandler.cs

internal class KestrelAuthHandler : IAuthenticationHandler 
{ 
    internal KestrelAuthHandler(HttpContext httpContext, ClaimsPrincipal user) 
    { 
     HttpContext = httpContext; 
     User = user; 
    } 

    internal HttpContext HttpContext { get; } 

    internal ClaimsPrincipal User { get; } 

    internal IAuthenticationHandler PriorHandler { get; set; } 

    public Task AuthenticateAsync(AuthenticateContext context) 
    { 
     if (User != null) 
     { 
      context.Authenticated(User, properties: null, description: null); 
     } 
     else 
     { 
      context.NotAuthenticated(); 
     } 


     if (PriorHandler != null) 
     { 
      return PriorHandler.AuthenticateAsync(context); 
     } 

     return Task.FromResult(0); 
    } 

    public Task ChallengeAsync(ChallengeContext context) 
    { 
     bool handled = false; 
     switch (context.Behavior) 
     { 
      case ChallengeBehavior.Automatic: 
       // If there is a principal already, invoke the forbidden code path 
       if (User == null) 
       { 
        goto case ChallengeBehavior.Unauthorized; 
       } 
       else 
       { 
        goto case ChallengeBehavior.Forbidden; 
       } 
      case ChallengeBehavior.Unauthorized: 
       HttpContext.Response.StatusCode = 401; 
       // We would normally set the www-authenticate header here, but IIS does that for us. 
       break; 
      case ChallengeBehavior.Forbidden: 
       HttpContext.Response.StatusCode = 403; 
       handled = true; // No other handlers need to consider this challenge. 
       break; 
     } 
     context.Accept(); 


     if (!handled && PriorHandler != null) 
     { 
      return PriorHandler.ChallengeAsync(context); 
     } 

     return Task.FromResult(0); 
    } 

    public void GetDescriptions(DescribeSchemesContext context) 
    { 
     if (PriorHandler != null) 
     { 
      PriorHandler.GetDescriptions(context); 
     } 
    } 

    public Task SignInAsync(SignInContext context) 
    { 
     // Not supported, fall through 
     if (PriorHandler != null) 
     { 
      return PriorHandler.SignInAsync(context); 
     } 

     return Task.FromResult(0); 
    } 

    public Task SignOutAsync(SignOutContext context) 
    { 
     // Not supported, fall through 
     if (PriorHandler != null) 
     { 
      return PriorHandler.SignOutAsync(context); 
     } 

     return Task.FromResult(0); 
    } 
} 

И в автозагрузку.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
    { 
     app.UseMiddleware<KestrelAuthenticationMiddleware>(); 

     app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions 
     { 
      Authority = Configuration[AppConstants.Authority], 
      RequireHttpsMetadata = false, 
      AutomaticChallenge = true, 
      ScopeName = Configuration[AppConstants.ScopeName], 
      ScopeSecret = Configuration[AppConstants.ScopeSecret], 
      AutomaticAuthenticate = true 
     }); 
     app.UseMvc(); 
    }