JwtSecurityTokenHandler().WriteToken(token) 在托管环境中抛出错误

Posted

技术标签:

【中文标题】JwtSecurityTokenHandler().WriteToken(token) 在托管环境中抛出错误【英文标题】:JwtSecurityTokenHandler().WriteToken(token) throwing error in hosted environment 【发布时间】:2021-11-10 18:27:28 【问题描述】:

我有一个 .NET 5 项目。我正在尝试在其中生成 JWT。这一行给我一个部署到 IIS Web 服务器的错误:JwtSecurityTokenHandler().WriteToken(token)

The encryption algorithm 'System.String' requires a key size of at least 'System.Int32' bits.
Key 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey', is of size: 'System.Int32'. (Parameter 'key')

我在其中添加了注释代码,以测试进入令牌生成的值是否正常,并且密钥长度为 16 个字符(我已经测试了更多但仍然失败)。

这在我的本地环境中运行良好。

有人知道为什么会这样吗?

var claims = new[]

    new Claim(JwtRegisteredClaimNames.Sub, user.Id),
    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
;

var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_jwtConfig.Value.SecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var token = new JwtSecurityToken(_jwtConfig.Value.Issuer,
    _jwtConfig.Value.Audience,
    claims,
    expires: DateTime.UtcNow.AddDays(30),
    signingCredentials: creds);

//return new JsonResult(new Dictionary<string, object>
//  
//     "secretkey", _jwtConfig.Value.SecretKey ,
//     "creds", creds.ToString() ,
//     "issuer", _jwtConfig.Value.Issuer ,
//     "audience", _jwtConfig.Value.Audience ,
//     "token", token.ToString() 
//  );


return new JsonResult(new Dictionary<string, object>
    
       "access_token", new JwtSecurityTokenHandler().WriteToken(token) ,
    );

来自Startup.cs的相关部分:

services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings"));

services.AddAuthentication()
    .AddCookie()
    .AddJwtBearer(options =>
    
        options.RequireHttpsMetadata = false;
        options.IncludeErrorDetails = true;

        var secretKey = Configuration.GetSection("JWTSettings:SecretKey").Value;
        var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

        options.TokenValidationParameters = new TokenValidationParameters
        
            ValidateIssuer = true,
            ValidIssuer = Configuration.GetSection("JWTSettings:Issuer").Value,
            ValidateAudience = true,
            ValidAudience = Configuration.GetSection("JWTSettings:Audience").Value,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,

        ;

    )

【问题讨论】:

_jwtConfig.Value.SecretKey 的值是多少?似乎价值来自当地环境。启动文件中secretKey的长度是多少? var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey)) 它的长度超过 16 个字符 @BruceZhang 是 HmacSHA256 所必需的,从 appsettings.json 文件中提取。当我取消注释注释部分时(我将其放入以便我可以看到它通过了什么),我可以看到它是正确的值。 【参考方案1】:

这似乎不是编码问题,更多的是网络主机的 .NET 配置。将应用程序部署为“框架依赖”会导致错误,但是“自包含”代码本身可以工作。

【讨论】:

【参考方案2】:

您似乎有错误IDX10653。

但您的异常消息未显示真实信息。请查看此article 了解详情。能否添加代码IdentityModelEventSource.ShowPII = true; 并提供真实的异常信息?

我想你会看到真正的异常信息并且你可以理解发生了什么。 您的 SecretKey 很可能少于 128 位。在这种情况下,只需尝试在配置文件中为参数JWTSettings:SecretKey 使用更长的值。所以here描述了类似的场景。

【讨论】:

【参考方案3】:

好的,我可能有解决方案,但我不太确定,如果可行,请告诉我:

我已经像这样直接在 Startup.cs 中配置了我的 JWT 令牌:

Startup.cs

public static void AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
    
        services.Configure<JwtSettings>(configuration.GetSection("JwtSettings"));

        // Identity stuff

        services.AddTransient<IAuthenticationService, AuthenticationService>();

        services.AddAuthentication(options =>
        
            options.DefaultAuthenticateScheme = "JwtBearer";
            options.DefaultChallengeScheme = "JwtBearer";
        )
            .AddJwtBearer("JwtBearer", o =>
            
                o.RequireHttpsMetadata = false;
                o.SaveToken = true;
                o.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuerSigningKey = true,
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ValidIssuer = configuration["JwtSettings:Issuer"],
                    ValidAudience = configuration["JwtSettings:Audience"],
                    ClockSkew = TimeSpan.FromMinutes(5),
                    IssuerSigningKey = new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(configuration["JwtSettings:Key"]))
                ;

                o.Events = new JwtBearerEvents()
                
                    OnAuthenticationFailed = c =>
                    
                        c.NoResult();
                        c.Response.StatusCode = 500;
                        c.Response.ContentType = "text/plain";
                        return c.Response.WriteAsync($"c.Exception.ToString() JwtBearerEvents in IdentityServiceRegistration");
                    ,
                    OnChallenge = context =>
                    
                        context.HandleResponse();
                        context.Response.StatusCode = 401;
                        context.Response.ContentType = "application/json";
                        var result = JsonConvert.SerializeObject("401 Not authorized");
                        return context.Response.WriteAsync(result);
                    ,
                    OnForbidden = context =>
                    
                        context.Response.StatusCode = 403;
                        context.Response.ContentType = "application/json";
                        var result = JsonConvert.SerializeObject("403 Not authorized");
                        return context.Response.WriteAsync(result);
                    ,
                ;
            );
    

这是我的 CreateUserAsync 和其他方法使用的令牌生成方法:

private async Task<JwtSecurityToken> GenerateToken(ApplicationUser user)
    
        var claims = new[]
        
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(JwtRegisteredClaimNames.Nbf,
                new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
            new Claim(JwtRegisteredClaimNames.Exp,
                new DateTimeOffset(DateTime.Now.AddYears(10))
                .ToUnixTimeSeconds().ToString())
        
        .Union(userClaims);

        var jwtSecurityToken = new JwtSecurityToken(
            new JwtHeader(
                new SigningCredentials(
                    new SymmetricSecurityKey(Encoding.UTF8.GetBytes("//secret key of 64 characters of capital letters and numbers")),
                    SecurityAlgorithms.HmacSha256)),
            new JwtPayload(claims));

        return jwtSecurityToken;
    

您应该能够从这样的网站生成密钥:SHA256 Generator

如果有帮助,请告诉我。

【讨论】:

以上是关于JwtSecurityTokenHandler().WriteToken(token) 在托管环境中抛出错误的主要内容,如果未能解决你的问题,请参考以下文章

JWTSecurityTokenHandler.ValidateToken() 啥时候真正有效?

JwtSecurityTokenHandler().WriteToken(token) 在托管环境中抛出错误

JwtSecurityTokenHandler 和 TokenValidationParameters

JwtSecurityTokenHandler.ValidateToken 抛出 Lifetime 验证失败异常

JwtSecurityTokenHandler().ValidateToken() :: 签名验证失败...在此上下文中不支持 sha256

为啥我们有两个用于 JWT 令牌 JwtSecurityTokenHandler 和 JsonWebTokenHandler 的类?