.NET C#实现JWT授权时持续出现签名密钥未找到错误
Bearer error="invalid_token", error_description="The signature key was not found" 问题描述
在C# .NET中实现JWT授权时,反复收到错误:Bearer error="invalid_token", error_description="The signature key was not found"。已将多数校验项设为false简化配置,但错误仍未解决。
相关代码与配置
身份验证与授权配置方法
public static void ApplyJwtServices(this IServiceCollection services, Action<AuthenticationOptions>? authenticationOptions = null, Action<AuthorizationOptions>? authorizationOptions = null) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); if (authenticationOptions != null) { services .AddAuthentication(authenticationOptions) .AddJwtBearer(options => { options.TokenValidationParameters = ValidationParameters; }); } else { services .AddAuthentication(auth => { auth.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = ValidationParameters; }); } if (authorizationOptions != null) { services .AddAuthorization(authorizationOptions); } else { services.AddAuthorization(); } }
已在Program.cs中调用UseAuthentication()、UseAuthorization(),并通过builder.Services.ApplyJwtServices();启用上述配置。
JWT Token生成代码
public static string Generate(Dictionary<string, object>? claims = null, DateTime? expiresAt = null, string? audience = null) { var tokenDescriptor = new SecurityTokenDescriptor { Expires = expiresAt ?? DateTime.UtcNow.AddHours(1), Claims = new Dictionary<string, object> { {JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()} }, SigningCredentials = new SigningCredentials(ValidationParameters.IssuerSigningKey, SecurityAlgorithms.HmacSha256), Audience = audience ?? default }; if (!string.IsNullOrEmpty(SignumIdGlobal.ApplicationSettings.JwtOptions.EncryptionKey)) { tokenDescriptor.EncryptingCredentials = new EncryptingCredentials( new SymmetricSecurityKey( Encoding.UTF8.GetBytes(SignumIdGlobal.ApplicationSettings.JwtOptions.EncryptionKey)), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256); } if (claims != null) { foreach (var claim in claims) { tokenDescriptor.Claims.Add(claim.Key, claim.Value); } } var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); }
appsettings配置
"SecretKey": "c2lnbnVtaWRqd3R0b2tlbnNlY3JldA==", "RequireAudience" : false, "ValidateIssuerSigningKey": false, "ValidateAudience": false, "ValidateIssuer": false, "RequireSignedTokens" : false
Token验证参数配置
new() { RequireAudience = SignumIdGlobal.ApplicationSettings.JwtOptions.RequireAudience, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(SignumIdGlobal.ApplicationSettings.JwtOptions.SecretKey)), TokenDecryptionKey = string.IsNullOrEmpty(SignumIdGlobal.ApplicationSettings.JwtOptions.EncryptionKey) ? null : new SymmetricSecurityKey( Encoding.UTF8.GetBytes(SignumIdGlobal.ApplicationSettings.JwtOptions.EncryptionKey)), ValidateIssuerSigningKey = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidateIssuerSigningKey, ValidateAudience = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidateAudience, ValidateIssuer = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidateIssuer, ValidAudiences = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidAudiences ?? new List<string>(), ValidIssuers = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidIssuers ?? new List<string>(), ValidateActor = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidateActor, ValidateLifetime = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidateLifetime, ValidTypes = SignumIdGlobal.ApplicationSettings.JwtOptions.ValidTypes ?? new List<string>(), RequireExpirationTime = SignumIdGlobal.ApplicationSettings.JwtOptions.RequireExpirationTime, RequireSignedTokens = SignumIdGlobal.ApplicationSettings.JwtOptions.RequireSignedTokens, TryAllIssuerSigningKeys = SignumIdGlobal.ApplicationSettings.JwtOptions.TryAllIssuerSigningKeys, IgnoreTrailingSlashWhenValidatingAudience = SignumIdGlobal.ApplicationSettings.JwtOptions .IgnoreTrailingSlashWhenValidatingAudience }
生成的JWT示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0YmNjZWNmOC1mNTNhLTQ3ZjMtYWQ3Yi1kYTE2Zjc4OWJmYTUiLCJuYmYiOjE2Nzg2Mzc1MDIsImV4cCI6MTY3ODY0MTEwMiwiaWF0IjoxNjc4NjM3NTAyfQ.0rihJAzJ3Yc0yiRSWXLYViSN0qmYWvTiShWy8Qq2iZg
解决方案
核心问题:密钥处理错误
你的SecretKey是Base64编码格式,但代码中直接用Encoding.UTF8.GetBytes()将Base64字符串本身转换为字节数组,而非解码Base64得到原始密钥。这导致生成Token和验证Token时使用的密钥不一致,触发签名密钥未找到的错误。
修复步骤
修正密钥解码逻辑
将验证参数中的IssuerSigningKey创建代码修改为Base64解码:IssuerSigningKey = new SymmetricSecurityKey( Convert.FromBase64String(SignumIdGlobal.ApplicationSettings.JwtOptions.SecretKey)),由于Token生成代码依赖
ValidationParameters.IssuerSigningKey,修正后生成签名时会自动使用正确的密钥。验证密钥匹配
用以下代码测试修正后的密钥能否验证示例Token:var tokenHandler = new JwtSecurityTokenHandler(); var validationParams = new TokenValidationParameters { IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String("c2lnbnVtaWRqd3R0b2tlbnNlY3JldA==")), ValidateIssuer = false, ValidateAudience = false, }; try { tokenHandler.ValidateToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0YmNjZWNmOC1mNTNhLTQ3ZjMtYWQ3Yi1kYTE2Zjc4OWJmYTUiLCJuYmYiOjE2Nzg2Mzc1MDIsImV4cCI6MTY3ODY0MTEwMiwiaWF0IjoxNjc4NjM3NTAyfQ.0rihJAzJ3Yc0yiRSWXLYViSN0qmYWvTiShWy8Qq2iZg", validationParams, out _); Console.WriteLine("Token验证通过"); } catch (Exception ex) { Console.WriteLine($"验证失败:{ex.Message}"); }确认中间件顺序
确保Program.cs中中间件调用顺序正确:app.UseAuthentication(); app.UseAuthorization();UseAuthentication()必须在UseAuthorization()之前,否则验证逻辑不会生效。检查配置一致性
即使ValidateIssuerSigningKey设为false,只要生成的Token带有签名,验证环节仍会校验签名有效性,因此必须保证密钥完全匹配。
内容的提问来源于stack exchange,提问作者MatejDodevski

