.net 6.0 web 上的 JWT 不记名令牌无效 api

Invalid JWT bearer token on .net 6.0 web api

我正在尝试对我的应用程序实施安全性并使用身份和 JWT 不记名令牌进行身份验证,但我总是大摇大摆地收到无效令牌响应。尝试了很多解决方案,但我仍然得到相同的响应。

[HttpPost("[action]")]
        public async Task<IActionResult> Login(LoginBindingModel login)
        {
            IActionResult actionResult;
            var user = await userManager.FindByEmailAsync(login.Email);
            if (user == null)
            {
                actionResult = NotFound(new {errors = new[] {$"User with email {login.Email} is not found."}});
            }
            else if (await userManager.CheckPasswordAsync(user, login.Password))
            {
                if(!user.EmailConfirmed)
                {
                    actionResult = BadRequest(new { errors = new[] { $"Email not confirmed." } });
                }
                else
                {
                    var token = GenerateTokenAsync(user);
                    actionResult = Ok(token);
                }
            }
            else
            {
                actionResult = BadRequest(new { errors = new[] { $"Password is not valid." } });
            }
           
            return actionResult;
        }

        private string GenerateTokenAsync(IdentityUser user)
        {
            IList<Claim> userClaims = new List<Claim>
            {
                new Claim("UserName", user.UserName),
                new Claim("Email", user.Email)
            };

            var x = jwtOptions.SecurityKey;
            return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
                claims: userClaims,
                expires: DateTime.UtcNow.AddMonths(1),
                signingCredentials: new SigningCredentials(jwtOptions.SecurityKey, SecurityAlgorithms.HmacSha256)));
        }

Program.cs

using ASPNetCoreMasterAPIAssignment2.Filters;
using DomainModels;
using Infrastructure.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Repositories;
using Services;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

builder.Services.AddSwaggerGen(x =>
{
    x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Description = "Please insert JWT token with bearer into field",
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey
    });
    x.AddSecurityRequirement(new OpenApiSecurityRequirement {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] { }
        }
    }); 
});

builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();

builder.Services.AddDbContext<ItemDbContext>(opt =>
{
    opt.UseSqlServer(builder.Configuration.GetConnectionString("default"));
});

builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<ItemDbContext>()
    .AddDefaultTokenProviders();

SecurityKey key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["jwt:secret"]));
builder.Services.Configure<JWTOptions>(_ => _.SecurityKey = key);

builder.Services.AddAuthentication(opt =>
{
    opt.DefaultAuthenticateScheme = "Bearer";
    opt.DefaultChallengeScheme = "Bearer";
    opt.DefaultScheme = "Bearer";
})
    .AddJwtBearer(opt =>
    {
        opt.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = false,
            IssuerSigningKey = key
        };
    });


var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseExceptionHandler("/error");
}

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "default": "Server=localhost;Database=EfCoreDb;Trusted_Connection=True;"
  },
  "jwt": {
    "secret": "5a927360-790b-4ba5-bae1-09aa98364090"
  }
}

当我将 [Authorized] 属性添加到控制器时,出现以下错误

invalid_token很多情况下都可能导致

尝试通过处理OnChallenge方法查看根本原因(特别是context.AuthenticateFailure的内容):

return builder.AddJwtBearer(options =>
{
    options.Authority = issuer;
    options.Audience = audience;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ClockSkew = new System.TimeSpan(0, 0, 30)
    };
    options.Events = new JwtBearerEvents()
    {
        OnChallenge = context =>
        {
            context.HandleResponse();
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            context.Response.ContentType = "application/json";

            // Ensure we always have an error and error description.
            if (string.IsNullOrEmpty(context.Error))
                context.Error = "invalid_token";
            if (string.IsNullOrEmpty(context.ErrorDescription))
                context.ErrorDescription = "This request requires a valid JWT access token to be provided";

            // Add some extra context for expired tokens.
            if (context.AuthenticateFailure != null && context.AuthenticateFailure.GetType() == typeof(SecurityTokenExpiredException))
            {
                var authenticationException = context.AuthenticateFailure as SecurityTokenExpiredException;
                context.Response.Headers.Add("x-token-expired", authenticationException.Expires.ToString("o"));
                context.ErrorDescription = $"The token expired on {authenticationException.Expires.ToString("o")}";
            }

            return context.Response.WriteAsync(JsonSerializer.Serialize(new
            {
                error = context.Error,
                error_description = context.ErrorDescription
            }));
        }
    };
});

来源:https://sandrino.dev/blog/aspnet-core-5-jwt-authorization#configuring-jwt-bearer-authentication

就我而言,它是通过更新一些 nuget 解决的,特别是 Microsoft.IdentityModel.Tokens:https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1792