因此,经过一些研究,正如我在评论中提到的,当您在“自托管”红隼下启动应用程序时,我发现 httpContext.Authentication.HttpAuthhenticationFeature.Handler 为空。但是当您使用 IIS 时,处理程序已由 Microsoft.AspNetCore.Server.IISIntegration.AuthenticationHandler 实例化。这个特定的处理程序实现是 Program.cs 中 .UseIISIntegration() 的一部分。
因此,我决定在我的应用程序中使用此实现的一部分并处理未经身份验证的请求。
对于我的 WebAPI(没有任何视图)服务,我使用在幕后使用 OAuth2IntrospectionAuthentication 和 JwtBearerAuthentication 的 IdentityServer4.AccessTokenValidation。
创建文件
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();
}