You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IIS共存JWT与Windows认证时JWT失效如何返回401而非登录弹窗

触发Windows认证流程的原因

  • 你自定义的JWT认证处理器在令牌校验失败时返回了AuthenticateResult.NoResult(),这个返回值的语义是当前处理器未找到对应认证凭据,将认证逻辑流转到后续注册的其他认证方案
  • 你在IIS侧同时启用了Windows身份验证,ASP.NET Core授权中间件检测到JWT方案未返回成功认证结果后,会触发IIS内置的Windows认证模块的挑战逻辑,返回带WWW-Authenticate: NTLM/Negotiate的响应头,浏览器收到后就会弹出Windows登录窗口
  • 二次调用HandleAuthenticateAsync是因为浏览器收到Windows认证挑战后,会自动携带本地Windows凭据重试请求,因此你的JWT处理器会被再次调用

实现JWT失败直接返回401的方案

方案1:修改自定义JWT处理器逻辑

将认证失败的返回值从AuthenticateResult.NoResult()改为AuthenticateResult.Fail(),同时重写挑战处理方法直接返回401,阻止认证逻辑流转到Windows方案:

public class BearerAuthenticationHandler
    : AuthenticationHandler<BearerAuthenticationSchemeOptions>
{
    private IJwtUtils _jwtUtils;
    public BearerAuthenticationHandler(
        IOptionsMonitor<BearerAuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        IJwtUtils jwtUtils)
        : base(options, logger, encoder, clock)
    {
        _jwtUtils = jwtUtils;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
        var user = _jwtUtils.ValidateJwtToken(token);

        if (user != null)
        {
            var ticket = new AuthenticationTicket(user, this.Scheme.Name);
            return AuthenticateResult.Success(ticket);
        }

        // 认证失败直接返回Fail,不再流转到其他认证方案
        return AuthenticateResult.Fail("无效或缺失JWT令牌"); 
    }

    // 重写挑战方法,直接返回401,不触发Windows认证弹窗
    protected override Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 401;
        Response.ContentType = "application/json";
        return Response.WriteAsync("{\"code\":401,\"message\":\"身份验证失败\"}");
    }
}

方案2:调整认证服务配置

显式指定所有认证相关的默认方案为JWT,禁止自动 fallback 到其他认证方案:

services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddScheme<BearerAuthenticationSchemeOptions, BearerAuthenticationHandler>(JwtBearerDefaults.AuthenticationScheme, null);

方案3(可选):调整IIS配置

如果不需要全局启用Windows认证,可在项目根目录的web.config中添加配置,让IIS不强制拦截未认证请求,所有认证逻辑交给ASP.NET Core代码处理:

<system.webServer>
  <security>
    <authentication>
      <windowsAuthentication enabled="true" />
      <anonymousAuthentication enabled="true" />
    </authentication>
    <authorization>
      <!-- 允许匿名访问,IIS不主动触发认证挑战 -->
      <add accessType="Allow" users="?" />
    </authorization>
  </security>
</system.webServer>

内容的提问来源于stack exchange,提问作者Семён Прокофьев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 09:57:03