【问题标题】:Authorization for anonymous user (automatic authentication)匿名用户授权(自动认证)
【发布时间】:2016-10-22 15:25:06
【问题描述】:

更新:不幸的是,Windows 重启解决了这个问题 -.-


在我们的 ASP.NET Core (1.0 RC2) 应用程序中,我们有以下要求:只有来自内部网络的用户才能访问一些“调试”页面(由 MVC Core 托管)。这是一个公共网站,我们没有用户登录,而是使用基于自定义 IP 地址的授权来管理它(注意:在我们的案例中,这不是安全风险,因为我们之间有一个代理,所以IP 地址不能被外部欺骗)。

我们也想在 ASP.NET Core 中实现这种基于 IP 地址的授权。我们为此使用自定义策略 "DebugPages",并在 MVC 控制器上使用相应的 [Authorize(Policy="DebugPages")] 定义。然后我们注意到,我们必须有一个经过身份验证的用户才能让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();
    });

    ...
}

现在,当在 Visual Studio 2015(使用 IIS Express)的调试中运行时,工作正常。 但不幸的是,当dotnet run(使用 Kestrel)从命令行直接运行时,它不起作用。在这种情况下,我们得到以下异常:

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

当我们提供当前的 Windows 主体而不是具有自定义匿名身份的主体时,会发生同样的错误 -- 所以每次当用户被自动-ally 验证时......

那么,为什么在 IIS Express 和 Kestrel 中托管存在差异?有什么建议可以解决这个问题吗?

【问题讨论】:

  • 这部分代码适用于我的 kestrel(dotnet run)。似乎异常的原因与此代码无关。
  • 该死...我刚刚重新启动,现在一切正常。我不知道发生了什么...好旧的 Windows,我担心(如果它不好 - 重新启动!)。
  • 你好。我有同样的问题。不是在 Windows 中,而是在 docker 中。版本为Core 1.0。我已经调查了这个问题,发现 context.Authentication.HttpAuthhenticationFeature.Handler 在您使用 Kestrel 时为空,而在您使用 IIS 时不为空。我认为诀窍在于 Program.cs 中的 .UseIISIntegration()

标签: c# asp.net-core asp.net-core-1.0 asp.net-authorization asp.net-authentication


【解决方案1】:

所以,经过一些研究,正如我在 cmets 中提到的,当您在“自托管”红隼下启动应用程序时,我发现 httpContext.Authentication.HttpAuthhenticationFeature.Handler 为空。但是当您使用 IIS 时,处理程序已由 Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler 实例化。这个特定的处理程序实现是 Program.cs 中 .UseIISIntegration() 的一部分。

因此,我决定在我的应用程序中使用此实现的一部分并处理未经身份验证的请求。

对于我的 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);
    }
}

在 Startup.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();
    }

【讨论】:

    猜你喜欢
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 2019-01-06
    • 2014-06-02
    相关资源
    最近更新 更多