首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Blazor WASM Hosted - Authorize on API总是返回UnAuthorized

Blazor WASM Hosted - Authorize on API总是返回UnAuthorized
EN

Stack Overflow用户
提问于 2020-12-22 00:11:06
回答 1查看 253关注 0票数 0

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

服务器- Starup.cs

代码语言:javascript
复制
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");
            });
        }
    }

客户端- Program.cs

代码语言:javascript
复制
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();
        }
    }

RolesClaimPrincipalFactory.cs

代码语言:javascript
复制
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;
        }
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-28 03:47:33

您可能会遇到这个问题,因为您使用的ICountryService具有自己的http客户端,该客户端没有配置为在传出请求中包含身份验证令牌--没有令牌,就没有访问权限。

我们可以通过向客户端添加AuthorizationMessageHandler来附加令牌,就像配置命名客户端(XXX.ServerAPI)一样。

尝试将键入的客户端帮助器方法更改为以下内容:

代码语言:javascript
复制
/* Client Program.cs */

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

您可能想要更改帮助器,以便仅将令牌包含到实际需要它们的客户端(如果您也将该帮助器用于其他客户端)

有关更多信息,请访问See the docs

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65396156

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档