Asp.net core Blazor WASM Hosted-Authorization on API始终返回UnAuthorized

Asp.net core Blazor WASM Hosted-Authorization on API始终返回UnAuthorized,asp.net-core,authorization,blazor,webassembly,Asp.net Core,Authorization,Blazor,Webassembly,我有一个blazor wasm托管的解决方案,它是使用角色身份验证设置的。但是,每当我向任何API控制器添加[Authorize]属性时,我都会得到一个401 Unauthorized属性。我知道用户具有适当的角色,因为UI正在显示和隐藏该角色的功能。这就好像没有将角色传递给API一样。我错过了什么 服务器-Starup.cs public class Startup { public Startup(IConfiguration configuration)

我有一个blazor wasm托管的解决方案,它是使用角色身份验证设置的。但是,每当我向任何API控制器添加[Authorize]属性时,我都会得到一个401 Unauthorized属性。我知道用户具有适当的角色,因为UI正在显示和隐藏该角色的功能。这就好像没有将角色传递给API一样。我错过了什么

服务器-Starup.cs

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.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            //Register the Datacontext and Connection String
            services.AddDbContext<DataContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddDatabaseDeveloperPageExceptionFilter();

            //Sets up the default Asp.net core Identity Screens - Use Identity Scaffolding to override defaults
            services.AddDefaultIdentity<ApplicationUser>( options =>
                    {
                        options.SignIn.RequireConfirmedAccount = true;
                        options.Password.RequireDigit = true;
                        options.Password.RequireLowercase = true;
                        options.Password.RequireUppercase = true;
                        options.Password.RequiredUniqueChars = 0;
                        options.Password.RequireNonAlphanumeric = false;
                        options.Password.RequiredLength = 8;
                        options.User.RequireUniqueEmail = true;
                    })
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<DataContext>();

            //Associates the User to Context with Identity
            services.AddIdentityServer()
                .AddApiAuthorization<ApplicationUser, DataContext>( options =>
                {
                    options.IdentityResources["openid"].UserClaims.Add(JwtClaimTypes.Role);
                    options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Role);
                });

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove(JwtClaimTypes.Role);

            //Adds authentication handler
            services.AddAuthentication().AddIdentityServerJwt();

            //Register Repositories for Dependency Injection
            services.AddScoped<ICountryRepository, CountryRepository>();

            services.AddControllersWithViews();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseMigrationsEndPoint();
                app.UseWebAssemblyDebugging();
            }
            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();
            }

            //AutoMigrates data
            dataContext.Database.Migrate();

            app.UseHttpsRedirection();
            app.UseBlazorFrameworkFiles();
            app.UseStaticFiles();

            app.UseSerilogIngestion();
            app.UseSerilogRequestLogging();

            app.UseRouting();

            app.UseIdentityServer();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapControllers();
                endpoints.MapFallbackToFile("index.html");
            });
        }
    }
公共类启动
{
公共启动(IConfiguration配置)
{
配置=配置;
}
公共IConfiguration配置{get;}
//此方法由运行时调用。请使用此方法将服务添加到容器中。
//有关如何配置应用程序的更多信息,请访问https://go.microsoft.com/fwlink/?LinkID=398940
public void配置服务(IServiceCollection服务)
{
//注册Datacontext和连接字符串
services.AddDbContext(选项=>
options.UseSqlServer(
GetConnectionString(“DefaultConnection”);
AddDatabaseDeveloperPageExceptionFilter();
//设置默认Asp.net核心标识屏幕-使用标识脚手架覆盖默认值
services.AddDefaultIdentity(选项=>
{
options.SignIn.RequireConfirmedAccount=true;
options.Password.RequireDigit=true;
options.Password.RequireLowercase=true;
options.Password.RequireUppercase=true;
options.Password.RequiredUniqueChars=0;
options.Password.RequireNonAlphanumeric=false;
options.Password.RequiredLength=8;
options.User.RequireUniqueEmail=true;
})
.AddRoles()
.AddEntityFrameworkStores();
//将用户与标识关联到上下文
services.AddIdentityServer()
.addapi授权(选项=>
{
options.IdentityResources[“openid”].UserClaims.Add(JwtClaimTypes.Role);
options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Role);
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove(JwtClaimTypes.Role);
//添加身份验证处理程序
services.AddAuthentication().AddIdentityServerJwt();
//为依赖项注入注册存储库
services.addScope();
services.AddControllersWithViews();
services.AddRazorPages();
}
//此方法由运行时调用。请使用此方法配置HTTP请求管道。
公共void配置(IApplicationBuilder应用程序、IWebHostEnvironment环境、DataContext DataContext)
{
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
app.UseWebAssemblyDebugging();
}
其他的
{
app.UseExceptionHandler(“/Error”);
//默认的HSTS值为30天。您可能希望在生产场景中更改此值,请参阅https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//自动迁移数据
dataContext.Database.Migrate();
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.useserlogingestion();
app.useserlogrequestlogging();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(端点=>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
MapFallbackToFile(“index.html”);
});
}
}
Client-Program.cs

public class Program
    {
        public static async Task Main(string[] args)
        {
            //Serilog 
            var levelSwitch = new LoggingLevelSwitch();
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.ControlledBy(levelSwitch)
                .Enrich.WithProperty("InstanceId", Guid.NewGuid().ToString("n"))
                .WriteTo.BrowserHttp(controlLevelSwitch: levelSwitch)
                .CreateLogger();

            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddHttpClient("XXX.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

            // Supply HttpClient instances that include access tokens when making requests to the server project
            builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("XXX.ServerAPI"));

            builder.Services.AddApiAuthorization()
                .AddAccountClaimsPrincipalFactory<RolesClaimsPrincipalFactory>();

            var baseAddress = new Uri($"{builder.HostEnvironment.BaseAddress}api/"); 

            void RegisterTypedClient<TClient, TImplementation>(Uri apiBaseUrl)
                where TClient : class where TImplementation : class, TClient
            {
                builder.Services.AddHttpClient<TClient, TImplementation>(client =>
                {
                    client.BaseAddress = apiBaseUrl;
                });
            }

            RegisterTypedClient<ICountryService, CountryService>(baseAddress);


            await builder.Build().RunAsync();
        }
    }
公共类程序
{
公共静态异步任务主(字符串[]args)
{
//塞里洛格
var levelSwitch=新的日志levelSwitch();
Logger.Logger=新的LoggerConfiguration()
.最低液位由(液位开关)控制
.Enrich.WithProperty(“InstanceId”,Guid.NewGuid().ToString(“n”))
.WriteTo.BrowserHttp(controlLevelSwitch:levelSwitch)
.CreateLogger();
var builder=WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add(“#app”);
builder.Services.AddHttpClient(“XXX.ServerAPI”,client=>client.BaseAddress=新Uri(builder.hostenEnvironment.BaseAddress))
.AddHttpMessageHandler();
//向服务器项目发出请求时,提供包含访问令牌的HttpClient实例
builder.Services.addScope(sp=>sp.GetRequiredService().CreateClient(“XXX.ServerAPI”);
builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory();
var baseAddress=新Uri($“{builder.hostenEnvironment.baseAddress}api/”;
无效RegisterTypedClient(Uri apiBaseUrl)
where TClient:class其中TImplementation:class,TClient
{
builder.Services.AddHttpClient(客户端=>
{
client.BaseAddress=apiBaseUrl;
});
}
RegisterTypedClient(基本地址);
等待builder.Build().RunAsync();
}
}
RolescaimPrincipalFactory.cs

public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
    {
        public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor)
        {
        }

        public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
            RemoteUserAccount account,
            RemoteAuthenticationUserOptions options)
        {
            ClaimsPrincipal user = await base.CreateUserAsync(account, options);

            if (user.Identity.IsAuthenticated)
            {
                var identity = (ClaimsIdentity)user.Identity;
                Claim[] roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

                if (roleClaims != null && roleClaims.Any())
                {
                    foreach (Claim existingClaim in roleClaims)
                    {
                        identity.RemoveClaim(existingClaim);
                    }

                    var rolesElem = account.AdditionalProperties[identity.RoleClaimType];

                    if (rolesElem is JsonElement roles)
                    {
                        if (roles.ValueKind == JsonValueKind.Array)
                        {
                            foreach (JsonElement role in roles.EnumerateArray())
                            {
                                identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                            }
                        }
                        else
                        {
                            identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
                        }
                    }
                }
            }

            return user;
        }
    }
公共类角色ClaimsPrincipalFactory:AccountClaimsPrincipalFactory
{
公共角色ClaimsPrincipalFactory(IAccessTokenProviderAccessor访问者):基本(访问者)