不记名令牌 401 错误 - asp.net 核心 3.1

401 error with bearer token - asp.net core 3.1

我无法使用 Asp.net Core 生成的令牌授权访问受保护的方法。

Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<ApplicationSettings>
            (Configuration.GetSection("ApplicationSettings"));
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<AppUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
        services.AddControllersWithViews();
        services.AddScoped<IRepository<Course>, GenericRepository<Course>>();
        services.AddRazorPages();
        // CORS - Configuration
        services.AddCors(opt =>
            opt.AddPolicy("CorsPolicy", policy =>
                         policy.AllowAnyHeader()
                               .AllowAnyMethod()
                               .AllowAnyOrigin()));
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo
            {
                Version = "v1",
                Title = "LMS Basic API",
                Description = "LMS Diamond for api",

            });
            //c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            //{
            //    Name = "Authorization",
            //    Type = SecuritySchemeType.ApiKey,
            //    Scheme = "Bearer",
            //    BearerFormat = "JWT",
            //    In = ParameterLocation.Header,
            //    Description = "JWT Authorization header using the Bearer scheme."
            //});
            //c.AddSecurityRequirement(new OpenApiSecurityRequirement
            //{
            //    {
            //          new OpenApiSecurityScheme
            //            {
            //                Reference = new OpenApiReference
            //                {
            //                    Type = ReferenceType.SecurityScheme,
            //                    Id = "Bearer"
            //                }
            //            },
            //            new string[] {}
            //    }
            //});
        });

        // JWT Auth
        var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());
        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = false;
            x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
            };
        });

    }

    // 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();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }
        app.UseHttpsRedirection();
        //app.UseRewriter(new RewriteOptions()
        //    .AddRedirectToHttpsPermanent());

        app.UseCors("CorsPolicy");

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "LMS Basic API");
            c.RoutePrefix = string.Empty;
        });
        app.UseStaticFiles();

        app.UseRouting();

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

登录操作时生成令牌

                    var tokenDescripter = new SecurityTokenDescriptor
                {
                    Subject = new System.Security.Claims.ClaimsIdentity(new Claim[] {
                    new Claim("UserID", user.Id.ToString())
                }),
                    Expires = DateTime.UtcNow.AddSeconds(20),
                    SigningCredentials = new SigningCredentials(new
                            SymmetricSecurityKey(Encoding.UTF8.GetBytes(applicationSettings.JWT_Secret)),
                            SecurityAlgorithms.HmacSha256Signature)
                };
                var tokenHandler = new JwtSecurityTokenHandler();
                var securityToken = tokenHandler.CreateToken(tokenDescripter);
                var token = tokenHandler.WriteToken(securityToken);

动作

       [HttpGet]
    [Authorize(AuthenticationSchemes = "Bearer")]
    [Route("GetUserData")]
    public async Task<IActionResult> GetUserData()
    {
        string userId = User.Claims.First(c => c.Type == "UserID").Value;
        var user = await _userManager.FindByIdAsync(userId);
        if (user == null)
        {
            return BadRequest();
        }
        return Ok(new
        {
            user.Id,
            user.FullName,
            user.Email,
        });
    }

我可以在 Postman 的 HTTP headers 中添加或不添加授权,我收到一个 "Unauthorized Exception - 401"

我已经检查了一些其他的Stack post和GitHub Post,看来我的配置没问题。

如果需要我可以添加配置文件。

提前致谢。

您需要确保正确发送承载身份验证

Authorization: Bearer <token>

查看示例 - Add Bearer Token to an API request

您的令牌声明需要包含 ClaimsIdentity.UserIdClaimType and/or ClaimsIdentity.UserNameClaimType(老实说,不确定是哪个),用户才能被识别为已通过身份验证。实现这一点的最简单方法是使用最终调用 UserClaimsPrincipalFactory.GenerateClaimsAsync()SignInManager.CreateUserPrincipalAsync(),例如这个帮手来自我的一个项目:

    public async Task<string> GetToken(ApplicationUser user, TimeSpan? expiresIn)
    {
        var principal = await signInManager.CreateUserPrincipalAsync(user);
        if (principal == null)
            return null;

        var signingKey = GetSecurityKey(configuration);

        var token = new JwtSecurityToken(
            expires: DateTime.UtcNow + expiresIn,
            claims: principal.Claims,
            signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
            );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

另一个选项似乎是您可以指定 IdentityOptions.ClaimsIdentity in config to teach Identity that UserID is the Claim Type it should use to identify your user (based on this code)。