首页 > 解决方案 > 当用户 id 类型从字符串更改为整数时,.net 核心中的 User.Identity.IsAuthenticated 始终为 false

问题描述

我有一个使用 json Web 令牌进行身份验证的 asp.net 核心应用程序,当我的用户 ID 是字符串时,这工作正常,但在更改为 int 后停止工作。

我的 IdentityUser 类型最初是这个

public class AppUser : IdentityUser
{
     //other properties...
}

我更新了这个;

public class AppUser : IdentityUser<int>
{
     //other properties...
}

我从这里更改了我的启动配置;

        services.Configure<JwtIssuerOptions>(options =>
        {
            options.Issuer = jwtIssuerOptions.Issuer;
            options.Audience = jwtIssuerOptions.Audience;
            options.SigningCredentials = jwtIssuerOptions.SigningCredentials;
        });


        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = jwtIssuerOptions.Issuer,

            ValidateAudience = true,
            ValidAudience = jwtIssuerOptions.Audience,

            ValidateIssuerSigningKey = true,
            IssuerSigningKey = _signingKey,

            RequireExpirationTime = false,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(configureOptions =>
        {
            configureOptions.ClaimsIssuer = jwtIssuerOptions.Issuer;
            configureOptions.TokenValidationParameters = tokenValidationParameters;
            configureOptions.SaveToken = true;
        });

        // add identity
        var builder = services.AddIdentityCore<AppUser>(o =>
        {
            // configure identity options
            o.Password.RequireDigit = false;
            o.Password.RequireLowercase = false;
            o.Password.RequireUppercase = false;
            o.Password.RequireNonAlphanumeric = false;
            o.Password.RequiredLength = 6;
            o.SignIn.RequireConfirmedEmail = true;
        });
        builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
        builder.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

对此;

    services.Configure<JwtIssuerOptions>(options =>
        {
            options.Issuer = jwtIssuerOptions.Issuer;
            options.Audience = jwtIssuerOptions.Audience;
            options.SigningCredentials = jwtIssuerOptions.SigningCredentials;
        });


        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = jwtIssuerOptions.Issuer,

            ValidateAudience = true,
            ValidAudience = jwtIssuerOptions.Audience,

            ValidateIssuerSigningKey = true,
            IssuerSigningKey = _signingKey,

            RequireExpirationTime = false,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(configureOptions =>
        {
            configureOptions.ClaimsIssuer = jwtIssuerOptions.Issuer;
            configureOptions.TokenValidationParameters = tokenValidationParameters;
            configureOptions.SaveToken = true;
        });

        // add identity
        var builder = services.AddIdentity<AppUser, IdentityRole<int>>(o =>
        {
            // configure identity options
            o.Password.RequireDigit = false;
            o.Password.RequireLowercase = false;
            o.Password.RequireUppercase = false;
            o.Password.RequireNonAlphanumeric = false;
            o.Password.RequiredLength = 6;
            o.SignIn.RequireConfirmedEmail = true;
        });
        builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole<int>), builder.Services);
        builder.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

我使用以下方法生成令牌,这在实现之间没有变化。

    public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
    {
        var claims = new[]
     {
             new Claim(JwtRegisteredClaimNames.Sub, userName),
             new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
             new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
             identity.FindFirst(JwtClaimIdentifiers.Rol),
             identity.FindFirst(JwtClaimIdentifiers.Id)
         };

        // Create the JWT security token and encode it.
        var jwt = new JwtSecurityToken(
            issuer: _jwtOptions.Issuer,
            audience: _jwtOptions.Audience,
            claims: claims,
            notBefore: _jwtOptions.NotBefore,
            expires: _jwtOptions.Expiration,
            signingCredentials: _jwtOptions.SigningCredentials);

        var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

        return encodedJwt;
    }

最后使用此方法创建声明身份;

    public ClaimsIdentity GenerateClaimsIdentity(string userName, int id)
    {
        return new ClaimsIdentity(new GenericIdentity(userName, "Token"), new[]
        {
            new Claim(JwtClaimIdentifiers.Id, id.ToString(), ClaimValueTypes.Integer32),
        });
    }

User.Identity.IsAuthenticated 现在在验证时始终为假,并且 ClaimsIdentity 似乎没有我在将我的 AppUser 从字符串 ID 类型转换为 int Id 类型之前看到的信息。

到目前为止,我拥有的最好的理论是,问题可能与令牌值的序列化/反序列化有关,但我正抓着稻草,几乎不知道如何调试它。

我错过了什么?

标签: c#asp.net-corejwtclaims

解决方案


在您的代码中没有什么让我感到眼花缭乱的(看起来主要的区别是您从 切换AddIdentityAddIdentityCore?),但我发现 JWT 身份验证问题,它确实有助于在调试模式下运行应用程序并为JwtBearerEvents.
将此添加到您的启动.AddJwtBearer电话中:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =>
    {
      // ... 

      // Add this at the end:
      options.Events = new JwtBearerEvents
      {
        OnAuthenticationFailed = context =>
        {
// Put a breakpoint here
          System.Console.WriteLine(context.Exception);
          return Task.CompletedTask;
        },
      };
    })

如果您在调试中运行,将能够检查context.Exception

示例异常检查

这应该可以更好地解释 JWT 身份验证失败的原因。


推荐阅读