ASP.NET Core 5 JWT 身份验证失败,响应代码为 401

Posted

技术标签:

【中文标题】ASP.NET Core 5 JWT 身份验证失败,响应代码为 401【英文标题】:ASP.NET Core 5 JWT Authentication fails with response code 401 【发布时间】:2021-12-06 14:47:00 【问题描述】:

我正在尝试在我的 ASP.NET Core 5 Web API 中实现基于 JWT 的身份验证。但是,当我使用标有 [Authorize] 属性的 API 时,我总是得到响应代码 401。

这是我目前所拥有的。首先,如果用户提供了有效的用户名和密码,我的AccountController 会发出 JWT:

[Authorize]
[ApiController]
[Route("api/" + Constants.ApiVersion + "/Accounts")]
public class AccountController : ControllerBase

  private readonly UserManager<AppUser>     _userManager;
  private readonly IPasswordHasher<AppUser> _passwordHasher;


  public AccountController(UserManager<AppUser> userManager, IPasswordHasher<AppUser> passwordHasher)
  
    _userManager    = userManager;
    _passwordHasher = passwordHasher;
  


  [AllowAnonymous]
  [HttpPost]
  [Route("Token")]
  public async Task<IActionResult> Login([FromForm]LoginBindingModel model)
  
    if(model == null)
    
      return BadRequest();
    

    if(!ModelState.IsValid)
    
      return BadRequest(ModelState);
    

    AppUser user = await _userManager.FindByNameAsync(model.UserName);

    if(user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
    
      return Unauthorized();
    

    SymmetricSecurityKey    encryptionKey   = new(Encoding.UTF8.GetBytes("TODO_Find_better_key_and_store_as_secret"));
    JwtSecurityTokenHandler jwtTokenHandler = new();
    SecurityTokenDescriptor tokenDescriptor = new()
    
      Subject            = new ClaimsIdentity(new[]  new Claim("UserName", user.UserName) ),
      Expires            = DateTime.UtcNow.AddDays(7),
      SigningCredentials = new SigningCredentials(encryptionKey, SecurityAlgorithms.HmacSha256Signature)
    ;

    SecurityToken jwtToken = jwtTokenHandler.CreateToken(tokenDescriptor);
    string        token    = jwtTokenHandler.WriteToken(jwtToken);

    return Ok(token);
  


  [HttpPost]
  [Route("ChangePassword")]
  public async Task<ActionResult> ChangePassword([FromBody]ChangePasswordBindingModel model)
  
    if(model == null)
    
      return BadRequest();
    

    if(!ModelState.IsValid)
    
      return BadRequest(ModelState);
    

    AppUser user = await _userManager.GetUserAsync(User);

    if(user == null)
    
      return new StatusCodeResult(StatusCodes.Status403Forbidden);
    

    IdentityResult result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);

    return GetHttpResponse(result);
  


  ...


这段代码似乎可以正常工作。它返回一个被jwt.io成功解析的令牌,并包含我输入的用户名。

接下来,Startup 类如下所示:

public class Startup

  public Startup(IConfiguration configuration)
  
    Configuration = configuration;
  


  public IConfiguration Configuration
  
    get;
  


  public void ConfigureServices(IServiceCollection services)
  
    services.Configure<ApplicationSettings>(Configuration.GetSection(nameof(ApplicationSettings)));
    services.AddIdentityCore<AppUser>(options =>
    
      Configuration.GetSection(nameof(IdentityOptions)).Bind(options);
    );
    services.AddScoped<IPasswordHasher<AppUser>, Identity.PasswordHasher<AppUser>>();
    services.AddTransient<IUserStore<AppUser>, UserStore>();
    services.AddAuthentication(options =>
    
      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    ).AddJwtBearer(options =>
    
      options.SaveToken = true;
      options.TokenValidationParameters = new TokenValidationParameters
      
        ValidateIssuer           = true,
        ValidIssuer              = "whatever",
        ValidateAudience         = true,
        ValidAudience            = "whatever",
        ValidateIssuerSigningKey = true,
        IssuerSigningKey         = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("TODO_Find_better_key_and_store_as_secret"))
      ;
    );
    services.AddMvc();
    services.AddControllers();
  


  // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  
    if(env.IsDevelopment())
    
      app.UseDeveloperExceptionPage();
    

    app.UseAuthentication();
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    
      endpoints.MapControllers();
    );
  

我正在向 Token 路由发送 HTTP POST 请求,该路由返回 JWT。之后,我将在请求正文中包含必要的 JSON 数据和标头中的 Authorization: Bearer &lt;the JWT&gt; 发送 HTTP POST 请求到 ChangePassword 路由。

但是,它总是返回响应代码 401,没有任何其他信息或异常。

我不知道Startup.ConfigureServices 中的魔法实际上应该在幕后做什么。无论如何,这显然是行不通的。有谁知道发生了什么以及如何使其发挥作用?

【问题讨论】:

【参考方案1】:

但是,它总是返回我的响应代码 401 没有任何 附加信息或例外情况。

那是因为您将ValidateIssuerValidateAudience 设置为true,但生成的令牌中没有IssuerAudience

一种方法是您可以在代码中设置IssuerAudience

SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor()

    Issuer= "whatever",
    Audience= "whatever",
    Subject = new ClaimsIdentity(new[]  new Claim("UserName", user.Name) ),
    Expires = DateTime.UtcNow.AddDays(7),
    SigningCredentials = new SigningCredentials(encryptionKey, SecurityAlgorithms.HmacSha256Signature)
;

另一种方式是可以设置ValidateIssuerValidateAudience为false:

services.AddAuthentication(options =>

    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
).AddJwtBearer(options =>

    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    
        ValidateIssuer = false,  //change here..
        ValidIssuer = "whatever",
        ValidateAudience = false,  //change here..
        ValidAudience = "whatever",
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("TODO_Find_better_key_and_store_as_secret"))
    ;
);

【讨论】:

您说的完全正确,这确实是问题所在。 JWT specification 声明 IssuerAudience 都是可选的并且是特定于应用程序的。因此,如果您在您的应用程序中没有使用它们,您不妨根本不使用它们。

以上是关于ASP.NET Core 5 JWT 身份验证失败,响应代码为 401的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 5.0 JWT 身份验证总是抛出 HTTP 401 代码

text 使用ASP.NET Core 2 Web API,Angular 5,.NET核心身份和Facebook登录进行JWT身份验证

ASP.NET Core 中的 Jwt 令牌身份验证

ASP.NET Core JWT 身份验证受众属性

ASP.NET Core JWT 身份验证更改声明(子)

ASP.net Core 2.0 AzureAd Bearer JWT-Token Auth 在验证签名时不会失败