【问题标题】:Authorization failing for custom authentication handler for ASP.NET Core 3.1?ASP.NET Core 3.1 的自定义身份验证处理程序授权失败?
【发布时间】:2020-11-09 07:22:09
【问题描述】:

我正在尝试实现一个简单的基于 api 密钥的身份验证处理程序。我的处理方法是

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
    // Get the apiKey from a store...
    if (apiKey != header.Parameter)
    {
        var error = "Invalid username or api key.";
        return Task.FromResult(AuthenticateResult.Fail(error));
    }

    var claims = new List<Claim> {new Claim("user", (string)username)};
    var identity = new ClaimsIdentity(claims);
    var principal = new ClaimsPrincipal(identity);
    var ticket = new AuthenticationTicket(principal, header.Scheme);

    return Task.FromResult(AuthenticateResult.Success(ticket));
}

当我使用正确的用户名和 api 密钥发出请求时,上述方法按预期返回 AuthenticateResult.Success(ticket)。但是,尽管经过了正确的身份验证,但我的控制器操作没有被调用。相反,Task HandleChallengeAsync(AuthenticationProperties properties) 被调用并返回 401 未经授权的响应。

我正在启动类中注册我的身份验证处理程序,例如:

public void ConfigureServices(IServiceCollection services)
{
    // register controllers, etc.
    services.AddAuthentication("ApiKey").AddApiKeyBearer();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
    IHostApplicationLifetime applicationLifetime)
{
    app.ConfigureExceptionHandler()
            .UseRouting()
            .UseAuthentication()
            .UseAuthorization()
            .UseEndpoints(builder => builder.MapControllers());
}

身份验证已经成功,如何避免挑战?

【问题讨论】:

    标签: asp.net-core authentication .net-core-3.1


    【解决方案1】:

    我认为在没有更多代码的情况下可能有点难以查明问题。我创建了一个应该根据您的要求工作的示例。请看下面的示例。

    编辑: 如果您不想手动配置以下设置,可以查看以下库: https://github.com/mihirdilip/aspnetcore-authentication-apiKey/tree/3.1.1

    处理程序

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    
    namespace WebApplication1.Handlers
    {
    
        public class ApiKeyAuthenticationSchemeOptions
          : AuthenticationSchemeOptions
        { }
    
        public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationSchemeOptions>
        {
            //TODO Change to whatever name you want to use
            private const string ApiKeyHeaderName = "X-Token";
    
            public ApiKeyAuthenticationHandler(
               IOptionsMonitor<ApiKeyAuthenticationSchemeOptions> options,
               ILoggerFactory logger,
               UrlEncoder encoder,
               ISystemClock clock)
               : base(options, logger, encoder, clock)
            {
            }
    
            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                if (!Request.Headers.ContainsKey(ApiKeyHeaderName))
                {
                    return Task.FromResult(AuthenticateResult.Fail("Header was not found"));
                }
    
                string token = Request.Headers[ApiKeyHeaderName].ToString();
    
                //TODO Replace with proper token handling code
                if (token == "secret")
                {
                    Claim[] claims = new[] {
                        new Claim(ClaimTypes.NameIdentifier, "john123"),
                        new Claim(ClaimTypes.Email, "john@gmail.com"),
                    };
    
                    ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, nameof(ApiKeyAuthenticationHandler));
                    AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), Scheme.Name);
    
                    return Task.FromResult(AuthenticateResult.Success(ticket));
                }
                else
                {
                    return Task.FromResult(AuthenticateResult.Fail("Token is invalid"));
                }
            }
        }
    }
    
    

    启动

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using WebApplication1.Handlers;
    
    namespace WebApplication1
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddAuthentication("ApiKey").AddScheme<ApiKeyAuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("ApiKey", op => { });
                services.AddControllers();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseHttpsRedirection();
    
                app.UseRouting();
    
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }
    
    

    控制器

    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using System.Linq;
    
    namespace WebApplication1.Controllers
    {
        [Route("api/students")]
        [ApiController]
        public class StudentsController : ControllerBase
        {
    
            [HttpGet("secret")]
            [Authorize]
            public IActionResult GetData()
            {
                string email = User.Claims.ElementAt(1).Value;
                return Ok("Secret data");
            }
    
            [HttpGet("public")]
            public IActionResult GetData2()
            {
                return Ok("Public data");
            }
    
        }
    }
    
    

    【讨论】:

    • 是否需要指定[Authorize(AuthenticationSchemes = "ApiKey")]认证方案?没有那个怎么办?
    • 不,您不必这样做。您可以将“ApiKey”设置为默认身份验证方案 - 这发生在 AddAuthentication() 方法中。但是请记住通过调用 app.UseAuthentication()(在 app.UseAuthorization() 之前)添加适当的中间件。我更新了上面的例子。
    【解决方案2】:

    我设法找到了答案here。基本上,我需要像这样覆盖启动类中的默认授权策略

    services.AddAuthorization(o =>
    {
        var builder = new AuthorizationPolicyBuilder("ApiKey");
        builder = builder.RequireClaim("user");
        o.DefaultPolicy = builder.Build();
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-29
      • 2018-10-26
      • 2021-04-16
      • 2022-08-18
      相关资源
      最近更新 更多