Asp.net 核心 3.1 授权无效!!!始终 return 401,即使提供了令牌

Asp.net core 3.1 Authorization not working!!! Always return 401 even when token is provided

我基本上是按照这个 youtube 视频上的说明操作 https://www.youtube.com/watch?v=s2zJ_g-iQvg&ab_channel=CodAffection 我的问题是我在使用 ASP.NET CORE 而他正在使用 ASP.NET CORE 2.2。出于某种原因,尽管我只更改了一些代码行以使其与 ASP.NET CORE 2.2.

兼容,但我的 api 总是调用 return 401

我想在用户登录时进行 API 调用 我正在使用 ASP.NET CORE 3.1。我做了一个有效的登录功能。当我与邮递员进行 API 调用时,登录函数 return 是一个令牌。但是,当我调用另一条路线(客户端)并提供不记名令牌时,我收到 401 错误。我已经搜索了几天,但无法解决问题。我尝试了许多不同的教程,但我一直遇到同样的问题。谢谢你的帮助。

[HttpPost, Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel user)
{
    var userFromDb = await _userManager.FindByNameAsync(user.UserName);
    if (user != null &&  true)//await _userManager.CheckPasswordAsync(user, user.Password))
    {
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[] {
                new Claim("UserID", "1")
            }),
            Expires = DateTime.UtcNow.AddHours(1),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey@45")), SecurityAlgorithms.HmacSha256Signature)
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var securityToken = tokenHandler.CreateToken(tokenDescriptor);
        var token = tokenHandler.WriteToken(securityToken);
        return Ok(new { token });
    }
    else 
    {
        return Unauthorized();
    }

}

这是我的启动文件的样子

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<ApplicationSettings>(Configuration.GetSection("AppSettings")); 
        services.AddMvc();

        services.AddTransient<DatabaseMigrator>();
        services.AddDbContext<erp_colombiaDbContext>(options => options.UseMySql(
                 Configuration.GetConnectionString("DefaultConnection"),
                 optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));
        services.TryAddScoped<UserManager<Employee>>();
        services.TryAddScoped<SignInManager<Employee>>();

        services.AddIdentityCore<Employee>(options => 
            options.SignIn.RequireConfirmedAccount = false
        ).AddEntityFrameworkStores<erp_colombiaDbContext>();

        //Jwt Authentication

        var key = Encoding.UTF8.GetBytes("superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey@45");//Configuration["AppSettings:Secret"].ToString());

        services.Configure<IdentityOptions>(options =>
        {
            options.Password.RequireDigit = false;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireUppercase = false;
            options.Password.RequiredLength = 4;
        });

        services.AddCors();

        //Jwt Authentication

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ClockSkew = TimeSpan.Zero
            };
        });

        // In production, the Angular files will be served from this directory
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/dist";
        });

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        if (!env.IsDevelopment())
        {
            app.UseSpaStaticFiles();
        }

        app.UseRouting();

        // global cors policy
        app.UseCors(x => x
            .AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader());


        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
        });

        app.UseSpa(spa =>
        {
            // To learn more about options for serving an Angular SPA from ASP.NET Core,
            // see https://go.microsoft.com/fwlink/?linkid=864501

            spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseAngularCliServer(npmScript: "start");
            }
        });
    }
}

这是我的 appSettings.json 设置的一部分。

  "AppSettings": {
    "Secret": "superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey@45",
    "ClientURL": "https://localhost:44344"
  },

这是另一条路线(客户)

// GET api/clients
[HttpGet]
[Authorize]
public IEnumerable<ClientViewModel> Get()
{
    ClientViewModel clientViewModel;
    List<ClientViewModel> listClientViewModels = new List<ClientViewModel>();

    var clients = Task.Run(async () => await _clientService.GetAllClients()).Result;

    foreach (var client in clients) 
    {
        clientViewModel = new ClientViewModel();
        clientViewModel.ClientId = client.ClientId;
        clientViewModel.Active = client.Active;
        clientViewModel.Address = client.Address;
        clientViewModel.City = client.City;
        clientViewModel.ClienteName = client.ClienteName;
        clientViewModel.ComercialEmployeeId = client.ComercialEmployeeId;
        clientViewModel.Confirmed = client.Confirmed;
        clientViewModel.CountryId = client.CountryId;
        clientViewModel.CreationDate = client.CreationDate;
        clientViewModel.DANE = client.DANE;
        clientViewModel.Department = client.Department;
        clientViewModel.ElectronicBillingEmail = client.ElectronicBillingEmail;
        clientViewModel.Eliminated = client.Eliminated;
        clientViewModel.NIT = client.NIT;
        clientViewModel.PostalCode = client.PostalCode;
        clientViewModel.Phone = client.Phone;

        listClientViewModels.Add(clientViewModel);
    }

    return listClientViewModels;
}

这是我在邮递员中添加令牌的方法。

修复你的启动,在app.UseEndpoints

之前添加
            ....
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
             .....

将 options.Events 添加到您的 AddJwtBearer:

.AddJwtBearer(options =>
{
  options.Events = new JwtBearerEvents
  {
    OnAuthenticationFailed = c =>
    {
      // break point here
      return Task.CompletedTask;
    },
  };
});

然后你会得到一些context.exception:

(右击图片,在新的window/tab中打开)

你应该永远不要在 asp.net

上这样做
// Task.Run is a no no
Task.Run(async () => await _clientService.GetAllClients()).Result;

您同时使用锁定 2 个线程没有任何好处。

改为:

// GET api/clients
[HttpGet]
[Authorize]
public Task<IEnumerable<ClientViewModel>> Get()

var clients = await _clientService.GetAllClients();

对于 .Net Core,您需要使用像 Identity Server 这样的授权提供程序:

https://identityserver4.readthedocs.io/en/latest/

我发现我有这段代码覆盖了 [Authorize] 属性。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = (User)context.HttpContext.Items["User"];
        if (user == null)
        {
            // not logged in
            context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
        }
    }
} 

我删除了它,现在可以使用了。我不明白为什么会有代码。抱歉,关于我的问题。