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

标签 c# asp.net-core jwt asp.net-core-webapi

我正在尝试在 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 成功解析的 token 。并包含我放入其中的用户名。

接下来,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 的路线。之后,我发送一个 HTTP POST 请求,请求正文中包含必要的 JSON 数据和 Authorization: Bearer <the JWT>在标题中添加到 ChangePassword路线。

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

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

最佳答案

However, that always returns me response code 401 without any additional information or exception.

那是因为你将ValidateIssuerValidateAudience设置为true,但是生成的结果中没有IssuerAudience token 。

一种方法是您可以在代码中设置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"))
    };
});

关于c# - ASP.NET Core 5 JWT 身份验证失败,响应代码为 401,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69634826/

相关文章:

c# - 将 Angular 应用程序分解为多个 Visual Studio 项目的方法

go - 无法获取额外信息。来自 gin-jwt 中的登录响应

带有加密 JWT 访问 token 的 Spring Boot OAuth2

c# - 设置 EF core 不为特定导航属性创建数据库外键

asp.net-core - 如何获取类库中的连接字符串?

c# - 枚举所有没有 mscoree 的 AppDomains

F# 中的 C# 扩展方法

security - 为什么在身份验证期间使用 JWT 刷新 token 可以防止 CSRF?

c# - 使用 linq 在 C# 中拆分和加入

c# - 如何填补列表中属性值的空白?