在 ASP.NET 核心应用程序中同时启用 Windows 身份验证和匿名身份验证
Enable both Windows authentication and Anonymous authentication in an ASP.NET Core app
我知道这已经被问过很多次了,但不幸的是,不是关于 ASP.NET 核心网络应用程序,而是经典的 ASP.NET 网络应用程序。我在 Internet 上找到的所有答案都对我没有帮助,因为 ASP.NET 核心应用程序的 IIS 配置与经典 ASP.NET 大不相同。例如,ASP.NET Core 使用了 Kestrel 代理,因此 ASP.NET 中的许多相关配置不在 ASP.NET Core 中。我基本上已经尝试了所有可能在 Internet 上找到的方法,但 none 帮助了我。我希望这就像在 IIS 中的应用程序上启用匿名和 windows 身份验证一样简单,仅此而已,但我想它会比这更有效。
在单个 asp.net 核心网络应用程序中启用这两种身份验证的过程是什么?
IIS 将充当反向代理,负责设置用户的 Windows 身份并将其传输给 Kestrel。所以首先,设置 IIS 以允许 Windows 和匿名身份验证:
然后,您需要更改 web.config 以要求 IIS 将 Windows 身份(以防找到)传输到您的 ASP.NET 核心应用程序,如下所示:
此时,如果您创建一个带有“[Authorize]”属性的控制器操作,HttpContext.User.Identity.Name;
应该具有您的客户端使用的 Windows 身份的值。我在这里回复了类似的内容:
好处是,如果您的客户端不传递 Windows 身份令牌,标准控制器操作仍然有效,而受保护的控制器操作(使用 [Authorize] 标签)将失败。
PS:我喜欢在详细模式下使用 curl.exe 来查看授权协议(协商协议、NTLM 令牌...)方面发生的情况
我有一个类似的 ASP.NET Core 2.0 应用程序场景(在整个应用程序中使用 Windows 身份验证,但单个控制器除外)并且 Daboul 的解释还不够。
我必须按照指示设置自定义中间件 here since 。
中间件
public class NtlmAndAnonymousSetupMiddleware
{
private readonly RequestDelegate next;
public NtlmAndAnonymousSetupMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated || context.Request.Path.ToString().StartsWith("/Anonymous"))
{
await next(context);
return;
}
await context.ChallengeAsync("Windows");
}
}
及其在Startup.cs中的用法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseMiddleware<NtlmAndAnonymousSetupMiddleware>();
// other code here
}
因此,中间件仅接受对 AnonymousController 的匿名请求,如果未提供 Windows 身份验证信息,则会提出质询。
匿名控制器
由于中间件区分了匿名和需要身份验证的内容,因此这看起来就像任何普通控制器:
[Route("Anonymous")]
public class AnonymousController : Controller
{
[HttpGet("Echo")]
public string Echo(string data)
{
return data;
}
}
测试
(全部在 Windows 机器上完成)
Chrome + 访问非匿名控制器操作 => 工作正常(@User.Identity.Name
和 @Context.User.Identity.Name
return 正确的用户
Chrome + 匿名操作 => 直接工作
Firefox(不直接从 OS 传输 NTLM 票证)+ 非匿名 => 模态要求 user/pass => 如果提供正确,它工作正常
Firefox + 匿名操作 => 直接工作
我有一个在 IIS 上禁用 Windows 身份验证的解决方案。您需要做的就是 NTLM 身份验证。只需将此递归代码放入您的控制器登录操作中:
var result = await HttpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
if (!result.Succeeded) {
await HttpContext.ChallengeAsync(IISDefaults.AuthenticationScheme); //performs NTLM handshake
return StatusCode(Response.StatusCode); // sends 401
}
// windows login has already succeed
// get user name and domain
WindowsIdentity winIdentity = (WindowsIdentity)result.Principal.Identity;
.......等等
如果有人想知道,我修改了@Alexei 的答案以使用属性而不是 Netcore 中的请求路径 3.X
首先创建 class 并获取端点元数据
public class NtlmAndAnonymousSetupMiddleware
{
private readonly RequestDelegate next;
public NtlmAndAnonymousSetupMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated || HasAnonymousAttribute(context))
{
await next(context);
return;
}
await context.ChallengeAsync("Windows");
}
private bool HasAnonymousAttribute(HttpContext context)
{
var endpoint = context.GetEndpoint();
var retVal = (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null);
return retVal;
}
}
然后修改public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<NtlmAndAnonymousSetupMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
当 IIS 不受我们控制时 - 可能是一种罕见的情况 - 我们甚至不知道 windows 身份验证是否已启用之前的解决方案不起作用,因为在我们到达之前抛出异常端点。
以下方法 - 既不优雅也不高效 - 在这种情况下可能会有所帮助。这个想法基本上是触发对端点的另一个调用以确定用户是否实际上是 windows 登录。如果此调用成功,则我们知道我们有一个 windows 用户并可以采取相应行动,例如重定向到需要 windows 身份验证的端点。但是,对于大多数情况,已经发布的解决方案可能是可行的方法,因为这个解决方案涵盖了边缘情况。
另请参阅:
代码示例 (.Net 6):
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> TestWindowsAuthAsync(CancellationToken cancellationToken)
{
using var client = new HttpClient(new HttpClientHandler()
{
UseDefaultCredentials = true
});
var response = await client.GetAsync($"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.PathBase}{HttpContext.Request.Path}/HasUserWindowsAuth");
if (response.IsSuccessStatusCode)
{
// Yes, now we know that user indeed has windows authentication and we can act upon it
return RedirectToAction("QuickLogin", input);
}
// No windows credentials have been passed at this point
return View();
}
[HttpGet("HasUserWindowsAuth")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public IActionResult HasUserWindowsAuth() => Ok();
[HttpGet("QuickLogin")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public async Task<IActionResult> QuickLoginAsync(LoginModel input, CancellationToken cancellationToken)
{
var user = this.User.Identities.FirstOrDefault(i => i System.Security.Principal.WindowsIdentity && i.IsAuthenticatd);
// do something with that user
}
我知道这已经被问过很多次了,但不幸的是,不是关于 ASP.NET 核心网络应用程序,而是经典的 ASP.NET 网络应用程序。我在 Internet 上找到的所有答案都对我没有帮助,因为 ASP.NET 核心应用程序的 IIS 配置与经典 ASP.NET 大不相同。例如,ASP.NET Core 使用了 Kestrel 代理,因此 ASP.NET 中的许多相关配置不在 ASP.NET Core 中。我基本上已经尝试了所有可能在 Internet 上找到的方法,但 none 帮助了我。我希望这就像在 IIS 中的应用程序上启用匿名和 windows 身份验证一样简单,仅此而已,但我想它会比这更有效。
在单个 asp.net 核心网络应用程序中启用这两种身份验证的过程是什么?
IIS 将充当反向代理,负责设置用户的 Windows 身份并将其传输给 Kestrel。所以首先,设置 IIS 以允许 Windows 和匿名身份验证:
然后,您需要更改 web.config 以要求 IIS 将 Windows 身份(以防找到)传输到您的 ASP.NET 核心应用程序,如下所示:
此时,如果您创建一个带有“[Authorize]”属性的控制器操作,HttpContext.User.Identity.Name;
应该具有您的客户端使用的 Windows 身份的值。我在这里回复了类似的内容:
好处是,如果您的客户端不传递 Windows 身份令牌,标准控制器操作仍然有效,而受保护的控制器操作(使用 [Authorize] 标签)将失败。
PS:我喜欢在详细模式下使用 curl.exe 来查看授权协议(协商协议、NTLM 令牌...)方面发生的情况
我有一个类似的 ASP.NET Core 2.0 应用程序场景(在整个应用程序中使用 Windows 身份验证,但单个控制器除外)并且 Daboul 的解释还不够。
我必须按照指示设置自定义中间件 here since
中间件
public class NtlmAndAnonymousSetupMiddleware
{
private readonly RequestDelegate next;
public NtlmAndAnonymousSetupMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated || context.Request.Path.ToString().StartsWith("/Anonymous"))
{
await next(context);
return;
}
await context.ChallengeAsync("Windows");
}
}
及其在Startup.cs中的用法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseMiddleware<NtlmAndAnonymousSetupMiddleware>();
// other code here
}
因此,中间件仅接受对 AnonymousController 的匿名请求,如果未提供 Windows 身份验证信息,则会提出质询。
匿名控制器
由于中间件区分了匿名和需要身份验证的内容,因此这看起来就像任何普通控制器:
[Route("Anonymous")]
public class AnonymousController : Controller
{
[HttpGet("Echo")]
public string Echo(string data)
{
return data;
}
}
测试
(全部在 Windows 机器上完成)
Chrome + 访问非匿名控制器操作 => 工作正常(
@User.Identity.Name
和@Context.User.Identity.Name
return 正确的用户Chrome + 匿名操作 => 直接工作
Firefox(不直接从 OS 传输 NTLM 票证)+ 非匿名 => 模态要求 user/pass => 如果提供正确,它工作正常
Firefox + 匿名操作 => 直接工作
我有一个在 IIS 上禁用 Windows 身份验证的解决方案。您需要做的就是 NTLM 身份验证。只需将此递归代码放入您的控制器登录操作中:
var result = await HttpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
if (!result.Succeeded) {
await HttpContext.ChallengeAsync(IISDefaults.AuthenticationScheme); //performs NTLM handshake
return StatusCode(Response.StatusCode); // sends 401
}
// windows login has already succeed
// get user name and domain
WindowsIdentity winIdentity = (WindowsIdentity)result.Principal.Identity;
.......等等
如果有人想知道,我修改了@Alexei 的答案以使用属性而不是 Netcore 中的请求路径 3.X
首先创建 class 并获取端点元数据
public class NtlmAndAnonymousSetupMiddleware
{
private readonly RequestDelegate next;
public NtlmAndAnonymousSetupMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.User.Identity.IsAuthenticated || HasAnonymousAttribute(context))
{
await next(context);
return;
}
await context.ChallengeAsync("Windows");
}
private bool HasAnonymousAttribute(HttpContext context)
{
var endpoint = context.GetEndpoint();
var retVal = (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null);
return retVal;
}
}
然后修改public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<NtlmAndAnonymousSetupMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
当 IIS 不受我们控制时 - 可能是一种罕见的情况 - 我们甚至不知道 windows 身份验证是否已启用之前的解决方案不起作用,因为在我们到达之前抛出异常端点。
以下方法 - 既不优雅也不高效 - 在这种情况下可能会有所帮助。这个想法基本上是触发对端点的另一个调用以确定用户是否实际上是 windows 登录。如果此调用成功,则我们知道我们有一个 windows 用户并可以采取相应行动,例如重定向到需要 windows 身份验证的端点。但是,对于大多数情况,已经发布的解决方案可能是可行的方法,因为这个解决方案涵盖了边缘情况。
另请参阅:
代码示例 (.Net 6):
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> TestWindowsAuthAsync(CancellationToken cancellationToken)
{
using var client = new HttpClient(new HttpClientHandler()
{
UseDefaultCredentials = true
});
var response = await client.GetAsync($"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.PathBase}{HttpContext.Request.Path}/HasUserWindowsAuth");
if (response.IsSuccessStatusCode)
{
// Yes, now we know that user indeed has windows authentication and we can act upon it
return RedirectToAction("QuickLogin", input);
}
// No windows credentials have been passed at this point
return View();
}
[HttpGet("HasUserWindowsAuth")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public IActionResult HasUserWindowsAuth() => Ok();
[HttpGet("QuickLogin")]
[Authorize(AuthenticationSchemes = IISDefaults.AuthenticationScheme)]
public async Task<IActionResult> QuickLoginAsync(LoginModel input, CancellationToken cancellationToken)
{
var user = this.User.Identities.FirstOrDefault(i => i System.Security.Principal.WindowsIdentity && i.IsAuthenticatd);
// do something with that user
}