ASP.NET核心JWT身份验证总是抛出401未授权

kq0g1dla  于 2023-01-27  发布在  .NET
关注(0)|答案(6)|浏览(362)

我试图尽可能简单地在我的www.example.com核心webAPI上实现JWT身份验证asp.net。我不知道我错过了什么,但它总是返回401,即使使用了***正确的***承载令牌。
下面是我configureServices代码

public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(
               x =>
               {
                   x.RequireHttpsMetadata = false;
                   x.SaveToken = true;
                   x.TokenValidationParameters = new TokenValidationParameters
                   {
                       ValidateIssuerSigningKey = true,
                       IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("A_VERY_SECRET_SECURITY_KEY_FOR_JWT_AUTH")),
                       ValidateAudience = false,
                       ValidateIssuer = false,
                   };
               }
                );
            services.AddControllers();

            services.AddDbContext<dingdogdbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("dingdogdbContext")));
        }

这就是我生成token的方法

[AllowAnonymous]
        [HttpPost("/Login")]
        public ActionResult<User> Login(AuthModel auth)
        {
            var user = new User();
            user.Email = auth.Email;
            user.Password = auth.Password;
            //var user = await _context.User.SingleOrDefaultAsync(u=> u.Email == auth.Email && u.Password==auth.Password);
            //if(user==null) return NotFound("User not found with this creds");

            //starting token generation...
            var tokenHandler = new JwtSecurityTokenHandler();
            var seckey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("A_VERY_SECRET_SECURITY_KEY_FOR_JWT_AUTH"));
            var signingCreds = new SigningCredentials(seckey, SecurityAlgorithms.HmacSha256Signature);
            var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
            {
                Subject = new System.Security.Claims.ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, user.Id.ToString()) }),
                SigningCredentials = signingCreds,
                Expires = DateTime.UtcNow.AddDays(7),
            });
            user.Token = tokenHandler.WriteToken(token);
            return user;
        }

我在app.useRouting()之后添加了app.useAuthorization()。当我向/Login发送POST请求时,我会获得令牌。但当我使用postman中的令牌查询任何其他端点时(在postman中的authorization/JWT中添加了令牌),每次都获得401未授权。还有什么我遗漏的吗?

eqzww0vc

eqzww0vc1#

请记住,UseAuthenticationUseRoutingUseAuthorization中间件必须正确,以便ASP框架正确地将身份上下文注入到http请求中。
它应该如下所示:(. NET核心版本3.1)
编辑:相同的代码适用于. NET 5和. NET 6

app.UseAuthentication();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
wwodge7n

wwodge7n2#

第一步:首先确定stratup.cs类中configure方法的顺序:
下面是www.example.com core3.1的有效订购单asp.net

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

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

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

如果步骤1不起作用,请尝试步骤2:确保令牌验证参数和令牌生成参数&算法相同,以便转到startup.cs类的ConfigureServices方法,并转到生成令牌的类或方法(在我的示例中是UserService类)

配置服务方法代码:

public void ConfigureServices(IServiceCollection services)
    {
        var connectionString = Configuration.GetConnectionString("mySQLConnectionString");

        services.AddDbContext<ApplicationDbContext>(options => options.UseMySql(connectionString));
        services.AddIdentity<IdentityUser, IdentityRole>(options =>
        {
            options.Password.RequireDigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequiredLength = 5;
        }).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

        services.AddAuthentication(auth =>
        {
            auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            
        }).AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidAudience = Configuration["AuthSettings:Audience"],
                ValidIssuer = Configuration["AuthSettings:Issuer"],
                RequireExpirationTime = true,
                IssuerSigningKey =
                    new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(Configuration["AuthSettings:key"])),
                ValidateIssuerSigningKey = true,

            };
        });
        services.AddScoped<IUserService, UserService>();
        services.AddControllers();
    }

令牌生成代码:

public async Task<UserManagerResponse> LoginUserAsync(LoginVIewModel model)
    {
        var user = await _userManager.FindByEmailAsync(model.Email);
        if(user == null)
        {
            return new UserManagerResponse
            {
                Message = "There is no user with that email",
                iSSuccess= false
            };
        }
        var result = await _userManager.CheckPasswordAsync(user, model.Password);
        if(! result)
        {
            return new UserManagerResponse
            {
                Message = "Your Provided password not match eith our system ",
                iSSuccess = false
            };

        }

        var clims = new[]
        {
            new Claim("Email", model.Email),
            new Claim(ClaimTypes.NameIdentifier, user.Id)
        };
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["AuthSettings:key"]));
        var token = new JwtSecurityToken(
            issuer: _configuration["AuthSettings:Issuer"],
            audience: _configuration["AuthSettings:Audience"],
            claims: clims,
            expires: DateTime.Now.AddDays(30),
            signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
            );
        string tokenAsString = new JwtSecurityTokenHandler().WriteToken(token);

        return new UserManagerResponse
        {
            Message = tokenAsString,
            iSSuccess = true,
            ExpireDate = token.ValidTo
        };
    }
}

还请注意,在我的情况下,我在appsetting.json中有一些拼写错误例如在令牌生成代码中,我调用了Audince,但在appSetting.json中它是Audience。这就是为什么两个Audience不匹配。

audience: _configuration["AuthSettings:Audince"]

应用设置.json代码:

"AllowedHosts": "*",
  "AuthSettings": {
    "key": "TThis is mfw sec test token",
    "Audience": "www.mfw.com",
    "Issuer": "www.mfw.com"
  }
dpiehjr4

dpiehjr43#

首先,您需要检查使用configureServices代码生成的JWT标记是否有效。要验证JWT标记,您可以使用JWT debugger。它会将JWT标记值解析为每个参数,您可以通过这些参数验证哪个参数值分配不正确,JWT调试器也会向您提供JWT有效或无效。一旦您弄清楚了这一点,您就可以处理已识别的错误或执行下一步操作。

1mrurvl1

1mrurvl14#

这里还有一些其他问题,您可能想要看一看并可能改进。登录机制目前包含一个7天到期的令牌。这意味着暴露的令牌仍将允许攻击者在7天内访问和模拟用户。一般来说,最好是:

  • 登录用户并生成仅在1小时内有效的令牌
  • 为用户提供永久设备令牌以表示设备
  • 验证设备和令牌(即使已过期),并可能生成新令牌。

这使用户能够在某些内容受到损害时“注销”所有会话,特别是大多数身份验证提供程序(如Auth0)或授权提供程序(如Authress)都提供这些功能和更多功能。

cnwbcb6i

cnwbcb6i5#

总结

在我的示例中,我没有使用任何Identity Server,但我提供了主机作为ValidIssuer。它验证了Authority的算法和密钥,但没有返回任何内容,这导致系统抛出未处理的异常。通过从AddJwtBearer(options => ...)中的JwtBearerOptions中删除options.Authority解决了此问题。
在那之后,我遇到了401错误,通过从AddJwtBearer(options => ...)中的JwtBearerOptions中删除options.Audience来解决它,还将ValidateLifetime添加到TokenValidationParameters(您可以在下面的第1部分中看到)
编号
第(1)部分JWT配置
在.NET 6中:

builder.services.AddAuthentication(options => 
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => 
 {
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters() 
    {
       ValidateIssuerSigningKey = jwtSettings.ValidateIssuerSigningKey,
       IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.IssuerSigningKey)),
       ValidateIssuer = jwtSettings.ValidateIssuer,
       ValidIssuer = jwtSettings.ValidIssuer,
       ValidateAudience = jwtSettings.ValidateAudience,
       ValidAudience = jwtSettings.ValidAudience,
       RequireExpirationTime = jwtSettings.RequireExpirationTime,
       ValidateLifetime = jwtSettings.RequireExpirationTime,
       ClockSkew = TimeSpan.FromDays(1),
    };
});

额外
使用以下任一位置从Appsettings获取JWT设置
“JSON Web令牌密钥”
configuration中节的名称:

var jwtSettings = new JwtSettings();
Configuration.Bind("JsonWebTokenKeys", jwtSettings);
builder.services.AddSingleton(jwtSettings);

//PART (1) => JWT Configuration goes here
//..
//..

或:

services.Configure<JwtSettings>(configuration.GetSection("JsonWebTokenKeys"));
using (ServiceProvider serviceProvider = services.BuildServiceProvider())
{
   var jwtSettings = serviceProvider.GetRequiredService<IOptions<JwtSettings>>().Value;
   
   //PART (1) => JWT Configuration goes here
   //..
   //.. 
}
nmpmafwu

nmpmafwu6#

我一次又一次地遇到同样的问题,我想通了,这是因为.AddJwtBearer()中的TokenValidationParameters必须像这样:

options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidateAudience = true,
        ValidAudience = builder.Configuration["Jwt:Audience"],
    };

只有在那种情况下,我才能让它工作。

相关问题