ASP.NET Core 3.1 的自定义身份验证处理程序授权失败?
Authorization failing for custom authentication handler for ASP.NET Core 3.1?
我正在尝试实现一个简单的 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 密钥发出请求时,returns AuthenticateResult.Success(ticket)
上面的方法如预期的那样。但是,尽管经过了正确的身份验证,但我的控制器操作并未被调用。相反,Task HandleChallengeAsync(AuthenticationProperties properties)
被调用并返回 401 未经授权的响应。
我正在启动时注册我的身份验证处理程序 class 例如:
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());
}
认证已经成功,如何避免质询?
我认为如果没有更多的代码,可能有点难以查明问题所在。我创建了一个应该根据您的要求工作的示例。看看下面的例子。
编辑:
如果您不想手动配置以下设置,您可以查看以下库:
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");
}
}
}
我找到了答案here。基本上,我需要像这样
覆盖启动 class 中的默认授权策略
services.AddAuthorization(o =>
{
var builder = new AuthorizationPolicyBuilder("ApiKey");
builder = builder.RequireClaim("user");
o.DefaultPolicy = builder.Build();
});
我正在尝试实现一个简单的 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 密钥发出请求时,returns AuthenticateResult.Success(ticket)
上面的方法如预期的那样。但是,尽管经过了正确的身份验证,但我的控制器操作并未被调用。相反,Task HandleChallengeAsync(AuthenticationProperties properties)
被调用并返回 401 未经授权的响应。
我正在启动时注册我的身份验证处理程序 class 例如:
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());
}
认证已经成功,如何避免质询?
我认为如果没有更多的代码,可能有点难以查明问题所在。我创建了一个应该根据您的要求工作的示例。看看下面的例子。
编辑: 如果您不想手动配置以下设置,您可以查看以下库: 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");
}
}
}
我找到了答案here。基本上,我需要像这样
覆盖启动 class 中的默认授权策略services.AddAuthorization(o =>
{
var builder = new AuthorizationPolicyBuilder("ApiKey");
builder = builder.RequireClaim("user");
o.DefaultPolicy = builder.Build();
});