如何将 Windows 身份验证和 JWT 与 .Net Core 2.1 结合使用

Posted

技术标签:

【中文标题】如何将 Windows 身份验证和 JWT 与 .Net Core 2.1 结合使用【英文标题】:How to combine the windows authentication and JWT with .Net Core 2.1 【发布时间】:2019-02-08 08:16:32 【问题描述】:

我已尝试将 windows 身份验证和 JWT 与 .NET Core 2.1 一起使用。

我有以下身份验证的启动设置:

services.AddAuthentication(options =>
                
                    options.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                )
                .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            
                options.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,

                    ValidIssuer = "Test",
                    ValidAudience = "Test",
                    IssuerSigningKey = JwtSecurityKey.Create("677efa87-aa4d-42d6-adc8-9f866e5f75f7")
                ;

                options.Events = new JwtBearerEvents()
                
                    OnAuthenticationFailed = OnAuthenticationFailed
                ;
            );

IIS 设置:

"iisSettings": 
    "windowsAuthentication": true, 
    "anonymousAuthentication": true, 
    ..
  

我已尝试使用以下代码 sn-p 创建具有 Windows 身份验证的 JWT 令牌:

[Route("api/[controller]")]
    [ApiController]
    [Authorize(AuthenticationSchemes = "Windows")]
    public class AuthController : ControllerBase
    
        [HttpPost("token")]
        public IActionResult Token()
        
            //Setup claims
            var claims = new[]
            
                new Claim(ClaimTypes.Name, User.Identity.Name),
                //Add additional claims
            ;

            //Read signing symmetric key
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("677efa87-aa4d-42d6-adc8-9f866e5f75f7"));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            //Create a token
            var token = new JwtSecurityToken(
                issuer: "Test",
                audience: "Test",
                claims: claims,
                expires: DateTime.Now.AddMinutes(30),
                signingCredentials: creds);

            //Return signed JWT token
            return Ok(new
            
                token = new JwtSecurityTokenHandler().WriteToken(token)
            );
        
    

在另一个控制器中我只需要使用 JWT 身份验证:

[Route("api/[controller]")]
    [ApiController]
    [Authorize(AuthenticationSchemes = "Bearer")]
    public class ProductController : ControllerBase
    
        [HttpGet]
        public IActionResult Get()
        
            var userName = User.Identity.Name;

            var claims = User.Claims.Select(x => new  x.Type, x.Value );

            return Ok(new  userName, claims );
        
    

如果 JWT 令牌已过期,那么我正确收到了响应代码 401,但我仍然会在浏览器中看到用于放置凭据的对话框。

当我想创建 JWT 令牌并禁用负责显示带有凭据的浏览器对话框的响应时,如何仅为部分配置 Windows 身份验证?如何正确组合这些东西?

【问题讨论】:

我正在努力解决完全相同的问题。似乎问题在于,在使用 Windows 身份验证时,IIS 将始终将“协商,NTLM”添加到身份验证响应标头值中。如果您在应用程序的中间件中检查响应,您只会看到“WWW-Authenticate Bearer”,但如果您在浏览器中检查响应,它已变为“WWW-Authenticate Bearer, Negotiate, NTLM”。浏览器看到这个并像它应该的那样弹出登录。一旦响应通过您的应用程序,这似乎由 IIS 完成。 【参考方案1】:

这是 IIS 的期望行为。一旦应用程序返回 401,IIS 就会将 WWW-Authenticate 附加到标头,这就是我们看到浏览器对话框输入凭据的原因。

更多:https://github.com/aspnet/Security/issues/1853

要解决此问题,我们需要将状态从 401 更改为其他状态,因此通过客户端框架(blazor 或 angular)我们可以将用户重定向到登录模块(调用 Authenticate Endpoint 以获取令牌)而不是显示凭据对话框。

最简单的方法是覆盖 JWT Bearer 的 OnChallenge 行为,并将状态从 401 更改为 600(或任何您喜欢的)。

services.AddAuthentication(options =>

  options.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
  options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddNegotiate()
 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,
        options =>
        
            options.TokenValidationParameters = new TokenValidationParameters
            
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidAudience = Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])),
                ClockSkew = TimeSpan.FromMinutes(25)
            ;
            options.Events = new JwtBearerEvents();
            // override the challenge behaviour and change the status to 600
            options.Events.OnChallenge = context =>
            

                context.HandleResponse();
                context.Response.StatusCode = (int)CustomHttpStatusCode.JwtTokenExpire;
                return Task.CompletedTask;
            ;

        );

【讨论】:

所以我有一个控制器,它根据用户的 Windows 帐户生成令牌。而其他所有的控制器只需要使用基于windows账号生成的bearer token,由于IIS的限制,这是不可能的?【参考方案2】:

这是我在 .net 5 中如何在 Web api 上同时使用 Windows 身份验证和 Jwt。我首先让 Windows 身份验证获取身份名称,然后检查数据库表以获取获取角色等的授权,然后返回一个 jwt 令牌。客户端网站使用 jwt 令牌访问受 jwt 保护的资源。

    在调试时在启动设置中启用匿名和 Windows 身份验证。如果部署在服务器上,则在 iis 中。

    如果您想通过 Kestrel 进行调试,在撰写本文时,在启动时,请勿使用 services.AddAuthentication(IISDefaults.AuthenticationScheme); 相反,使用

     services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
    

    在启动 configureservices 中设置标准以添加 jwt 身份验证。在配置函数中,添加身份验证。

    用这个装饰你想要使用 Windows 身份验证的函数:

    [Authorize(AuthenticationSchemes = NegotiateDefaults.AuthenticationScheme)]
    [Route("GetToken")]
    [HttpGet]
    public IActionResult GetToken() 
    
    用这个装饰你想要使用 jwt 令牌的控制器:
    [Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme)]
    [ApiController]
    [Route("[controller]")]
    public class ReportController : ControllerBase
    
    

就是这样。

【讨论】:

是的,那些特定的授权方案参数是我需要的。谢谢。 当未经身份验证的请求发送到 JWT 端点时,这不提供隐藏浏览器身份验证对话框的解决方案。【参考方案3】:

在 .Net 5.0 中,我已经能够在单个应用程序中成功处理 Windows、OIDC、Cookie 和 JWT 方案。 IIS/LaunchSettings.json 配置必须将 Anonymous 和 Windows 身份验证都指定为 true,因为如果没有针对 Anonymous 的服务器配置,承载身份验证将失败,并且如果没有针对 Windows 的服务器配置,Windows 身份验证将无法工作。

从 AllowAnonymous api 开始并发出响应重定向(重定向是此处的关键,以强制非匿名身份验证方案发出质询,并且在 Windows 无缝接收所需协商授权标头的情况下)到您需要的任何其他路由支持,用适当的授权方案属性装饰它们。

我更喜欢从每个非 jwt 方案中收集相关数据,使用令牌作为声明生成一个新的声明身份,并让除应用程序使用的身份验证控制器之外的所有其他路由都使用承载方案。我发现有必要在授权实现中同时覆盖 AllowAnonymous 和 Authorize 属性,以确保始终确保所有请求都被转换为具有有效不记名令牌的 ClaimsIdentity,以实现所有上下文的一致性。

为了支持 Windows 身份验证上下文的匿名上下文,我提供了一个标头,用于控制是尝试无缝身份验证还是简单地将 ClaimsIdentity 生成为匿名用户。在初始化声明身份以完成此操作时,请注意将身份验证类型设置为空字符串。

【讨论】:

【参考方案4】:

要同时使用 Windows 和 JWT 不记名身份验证——默认情况下,Windows 身份验证会应用于所有页面,它会覆盖 JWT 不记名的功能。 将两者结合到一个单个应用程序:-

    在用于生成 JWt Bearer Token 的提供程序上应用 Windows 身份验证,在其上使用 [AllowAnonymous] 标签,默认情况下使用 Windows 身份验证 使用 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 在应用程序的其余页面上应用 Jwt Bearer 身份验证

因此,在上述情况下,我们使用 Windows 身份验证来生成 JWT 令牌,并使用该 JWT 令牌来验证其余页面 在应用中。

对于测试场景,您可以尝试使用 NTLM(windows 身份验证)的 postman 进行令牌生成控制器和带有 JWTAuthentication 的页面的 BearerToken

【讨论】:

【参考方案5】:

到目前为止,我发现完成这项工作的唯一方法是使用 Kestrel。它允许我控制为每个请求发送哪些身份验证方案。在 IIS 上,我只控制 Bearer 是否发送,NTLM 和 Negotiate 是默认发送的。

【讨论】:

【参考方案6】:

我的处理方式是创建两个不同的 Web 应用程序:一个用于 Windows 身份验证,一个使用 JWT 令牌身份验证。

Windows 身份验证 Web 应用程序非常小,并且只做一件事。在端点通过 Windows 身份验证对用户进行身份验证并返回 JWT 令牌。

然后,该令牌可用于主应用程序。只要您的签名密钥和受众相同,令牌是否在不同的 Web 应用程序上创建都没有关系。

您无需为同时处理这两个问题而苦恼。

【讨论】:

【参考方案7】:

这个答案可能会有所帮助:https://***.com/a/51055082/1212994

您需要确保在尝试使用 Windows 身份验证时未设置 Authorization: Bearer HTTP 标头。这里的关键点是“Windows Auth”实际上是如何工作的。让我们以浏览器为例来看看它是如何工作的。

【讨论】:

这涵盖了相反的情况。当 OP 不想要它时,他们会收到 Windows Auth 提示。

以上是关于如何将 Windows 身份验证和 JWT 与 .Net Core 2.1 结合使用的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot with Spring Boot:将基本身份验证与JWT令牌身份验证相结合[复制]

JWT 身份验证 ASP.NET Core MVC 应用程序

Python机器对机器用户级jwt身份验证与框

如何使用JWT进行身份验证与授权

使用 JWT 进行环回身份验证

如何在 KONGO 中设置 JWT 身份验证