c# - 匿名用户授权(自动认证)

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

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


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

我们也想在 ASP.NET Core 中实现这种基于 IP 地址的授权。我们为此使用自定义策略 "DebugPages",并在 MVC Controller 上使用相应的 [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 中的托管存在差异?有什么解决问题的建议吗?

最佳答案

因此,经过一些研究,正如我在评论中提到的,我发现当您在“selfhosted”kestrel 下启动应用程序时,httpContext.Authentication.HttpAuthenticationFeature.Handler 为空。但是当您使用 IIS 时,处理程序已由 Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler 实例化。这个特定的处理程序实现是 Program.cs 中 .UseIISIntegration() 的一部分。

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

对于我的 WebAPI(没有任何 View )服务,我使用 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();
    }

关于c# - 匿名用户授权(自动认证),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37938333/

相关文章:

c# - "Incorrect syntax near ' = '"运行时错误 c# asp.net

c# - 在 AvalonEdit 中突出显示所有出现的选定单词

c# - 从同一个表单同时保存多行 - dotnet core

asp.net-core - 处理 Hangfire 内部的异常

请求线程外部的 ASP.NET Core RC2 SignalR Hub 上下文

asp.net-core - 在 ASP.NET Identity 中将电子邮件设置为用户名

c# - 根据十六进制值从字符串中删除特定字符

c# - 为什么 Visual Studio 需要特定版本的 sqlserver.types?

c# - ASP.NET Core MVC 中的 HttpContext.Timestamp 在哪里?

c# - 不能在 asp.net vnext core 中使用动态类型