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

ASP.NET Core 8 JWT生成异常致REST客户端认证失败排查

ASP.NET Core 8 JWT认证在API接口返回401重定向的问题

问题背景

已为ASP.NET Core 8 Web应用配置JWT令牌认证,该配置在带[Authorize]特性的MVC视图控制器中正常工作,但在REST API接口中,客户端(.NET Framework 4.0 WPF应用)通过请求头携带Bearer令牌认证时,返回401重定向页面的HTML,认证失败。Postman也能复现该问题。

认证配置代码(Startup.cs)

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
   options.SaveToken = true;
   options.RequireHttpsMetadata = false;
   options.TokenValidationParameters = new TokenValidationParameters
   {
       ValidateIssuer = true,
       ValidateAudience = true,
       ValidateLifetime = true,
       ValidateIssuerSigningKey = true,
       ClockSkew = TimeSpan.Zero,

       ValidAudience = Configuration["Jwt:Audience"],
       ValidIssuer = Configuration["Jwt:Issuer"],
       IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])),
   };
});

令牌生成代码

[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> Index(LoginUserUser usr)
{
    IActionResult response = Unauthorized();

    var user = await userManager.FindByNameAsync(usr.username);

    if (user != null && await userManager.CheckPasswordAsync(user, usr.password))
    {
        var userRoles = await userManager.GetRolesAsync(user);

        var authClaims = new List<Claim>
        {
            new(ClaimTypes.Name, user.UserName),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        };

        foreach (var userRole in userRoles)
        {
            authClaims.Add(new Claim(ClaimTypes.Role, userRole));
        }

        var token = CreateToken(authClaims);
        var refreshToken = GenerateRefreshToken();

        user.RefreshToken = refreshToken;
        var refreshTokenValidityInDays = Convert.ToInt32(Configuration["Jwt:RefreshTokenValidityInDays"]);
        user.RefreshTokenExpiryTime = DateTime.Now.AddDays(refreshTokenValidityInDays);

        await userManager.UpdateAsync(user);

        response = Ok(new
        {
            access_token = new JwtSecurityTokenHandler().WriteToken(token)
        });
    }

    return response;
}

private JwtSecurityToken CreateToken(List<Claim> authClaims)
{
    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var result = new JwtSecurityToken(
      issuer: Configuration["Jwt:Issuer"],
      audience: Configuration["Jwt:Audience"],
      claims: authClaims,
      expires: DateTime.Now.AddMinutes(1440),
      signingCredentials: credentials);

    return result;
}

排查进展

  • 已验证令牌有效:通过jwt.ms确认签名验证通过,Issuer和Audience与配置一致(使用本地应用URL)
  • 确认客户端请求携带的令牌正确

排查方向

  • 检查中间件顺序:确保app.UseAuthentication()在app.UseAuthorization()之前,且位于app.UseRouting()之后。若项目同时启用MVC Cookie认证,需避免其默认重定向逻辑干扰API接口。
  • 禁用JWT挑战重定向:API接口应返回JSON格式401而非HTML页面,可在JWT配置中添加事件处理:
    options.Events = new JwtBearerEvents
    {
        OnChallenge = context =>
        {
            context.HandleResponse();
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            context.Response.ContentType = "application/json";
            return context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(new { message = "Unauthorized" }));
        }
    };
    
  • 指定API控制器的认证方案:在API控制器的[Authorize]特性中明确指定JWT方案,避免默认使用Cookie认证:[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
  • 校验请求头格式:确认客户端请求的Authorization头严格遵循Bearer {token}格式,无多余空格或拼写错误。
  • 核对协议一致性:确保令牌中的Issuer/Audience协议(http/https)与请求协议一致,避免因协议不匹配导致验证失败。

内容的提问来源于stack exchange,提问作者Igor Kondrasovas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 00:45:16