Blazor WebAssembly - 找不到适合类型“JwtSecurityTokenHandler”的构造函数

Posted

技术标签:

【中文标题】Blazor WebAssembly - 找不到适合类型“JwtSecurityTokenHandler”的构造函数【英文标题】:Blazor WebAssembly - Suitable constructor for type 'JwtSecurityTokenHandler' could not be located 【发布时间】:2020-10-23 22:49:00 【问题描述】:

编辑:10-27-20 我正在重写这篇文章,因为经过一个多星期后,我现在对问题和解决方案都有了更好的理解。 第一个问题: 暴击:Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] 未处理的异常呈现组件:找不到适合类型“System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler”的构造函数。确保类型是具体的,并且为公共构造函数的所有参数注册了服务。 System.InvalidOperationException:找不到适合类型“System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler”的构造函数。确保类型是具体的,并且为公共构造函数的所有参数注册了服务。 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Microsoft.Extensions.DependencyInjection.ServiceLookup.ResultCache 生命周期,System.Type serviceType,System.Type implementationType,Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain)在:0

... 最初我虽然这与 Azure 相关联,但事实并非如此。它与在 Release 而非 Debug 下编译我的项目有关。

下面的旧选择 - 请忽略并跳转到答案 =>

当我在我的计算机上测试我的 Blazor 应用程序时,我没有收到任何错误,但是一旦我发布到我的 Azure 应用程序服务,我就会收到很长的错误集...

暴击:Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] 未处理的异常呈现组件:找不到适合类型“System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler”的构造函数。确保类型是具体的,并且为公共构造函数的所有参数注册了服务。 System.InvalidOperationException:找不到适合类型“System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler”的构造函数。确保类型是具体的,并且为公共构造函数的所有参数注册了服务。 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Microsoft.Extensions.DependencyInjection.ServiceLookup.ResultCache 生命周期,System.Type serviceType,System.Type implementationType,Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteChain callSiteChain)在:0

...

我了解注入系统正在寻找 JwtSecurityTokenHandler,但无法找到。我对没有 Web UI 的旧 MVC API 使用相同的配置,它工作得很好,就像我说它在我的本地系统上工作,但在 Azure 上我得到一个错误。我知道我遗漏了一些简单的东西,但我找不到。

感谢您查看这个。

public void ConfigureServices(IServiceCollection services)


     //Other DI Here


     services.AddScoped<JwtSecurityTokenHandler>();


        services.AddAuthentication(options =>
            
                // Identity made Cookie authentication the default.
                // However, we want JWT Bearer Auth to be the default.
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            )
            .AddJwtBearer(cfg  =>
            
                // Configure JWT Bearer Auth to expect our security key
                cfg.TokenValidationParameters =
                    new TokenValidationParameters
                    
                        LifetimeValidator = (before, expires, token, param) =>
                        
                            return expires > DateTime.UtcNow;
                        ,
                        ValidateAudience = true,
                        ValidateIssuer = true,
                        ValidateLifetime = true,
                        ValidIssuer = Configuration["JwtIssuer"],
                        ValidAudience = Configuration["JwtAudience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecretKey"]))
                    ;
            );

                  services.AddScoped<IInventoryAdjustmentRepository, InventoryAdjustmentRepository>();
        services.AddScoped<IEmployeeDataRepository, EmployeeDataRepository>();
        services.AddScoped<IPartDataRepository, PartDataRepository>();
        services.AddScoped<IInventoryScheduleRepository, InventoryScheduleRepository>();

 

编辑:客户端配置

        var builder = WebAssemblyHostBuilder.CreateDefault(args);

        builder.Services.AddScoped<JwtSecurityTokenHandler>();

        builder.Services.AddScoped<ApiAuthStateProvider, ApiAuthStateProvider>();
        builder.Services.AddScoped<AuthenticationStateProvider>(p => p.GetService<ApiAuthStateProvider>());

        builder.Services.AddTransient<IAuthenticationContract, UserServices>();
        builder.Services.AddTransient<IEmployeeDataContract, EmployeeService>();
        builder.Services.AddTransient<IPartRepository, PartRepository>();
        builder.Services.AddTransient<IInventorySchedule, InventorySchedule>();
        builder.Services.AddSingleton<IBrowserLocalStorage, BrowserLocalStorage>();
        builder.Services.AddSingleton<IBrowserSessionStorage, BrowserSessionStorage>();

        builder.Services.AddAuthorizationCore();
        
        builder.Services.AddSyncfusionBlazor();
        //Syncfusion.Licensing...

        builder.RootComponents.Add<App>("app");

        builder.Services.AddScoped(sp => new HttpClient  BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) );

       
        builder.Services.AddApiAuthorization();

    
        await builder.Build().RunAsync();

【问题讨论】:

您已经按照this question and answer 建议的方式进行操作了……所以我唯一可以贡献的就是指出用户曾对其他问题的评论……他说:“你不应该注入它。JwtSecurityTokenHandler 有一个无参数的构造函数,不是一次性的,并且具有与其父级相同的生命周期。只是new 它“......我无法提供任何想法或理由为什么它在本地工作但不在 Azure 中。 感谢您的建议。我也读到了,对反应感到困惑。确实,您使用新令牌创建了令牌,但这个问题似乎出在 DI 中,而不是创建令牌。话虽如此,我认为问题出在客户端应用程序上,而不是服务器 API 上。我可以使用邮递员获取令牌并访问服务器上的授权 API 端点。我正在调查客户端的 DI。这是我第一次使用 Blazor Web 程序集,开始时我选择了“一体化”模板。我将很快用我的客户端配置编辑我的帖子,看看这是否是我的问题。 【参考方案1】:

编辑:10-27-20 我的项目是默认的 Blazor WebAssembly (PWA),没有身份验证和 .net 核心托管后端。身份验证是使用 JwtSecurityTokenHandler 作为其主要身份验证创建的 为了缩小范围,问题出在客户端 (UI) 代码中。为了解决客户端内部的问题,我做了如下改动。

解决方案: 在重载的 AuthenticationStateProvider 中,我创建了一个新的令牌处理程序,而不是像原来那样使用 DI。

 public class ApiAuthStateProvider : AuthenticationStateProvider
 
        //Here was the 1st issue
        private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler();

        private readonly IBrowserLocalStorage _localStorage;

        private readonly HttpClient client;

        private readonly IBrowserSessionStorage _sessionStorage;

        public ApiAuthStateProvider(IBrowserLocalStorage localStorage, HttpClient _client, IBrowserSessionStorage sessionStorage)
        
            client = _client;
            _sessionStorage = sessionStorage;
            _localStorage = localStorage;
         

       ...

接下来我在https://github.com/mono/linker/issues/870 上发现了其他人遇到类似问题的地方。 它指示在主程序中添加以下内容将防止链接器杀死 JwtSecurityTokenHandler:

        _ = new JwtHeader();

        _ = new JwtPayload();

最后,在主代码中我删除了以下行,因为我不再使用 DI:

builder.Services.AddScoped<JwtSecurityTokenHandler>();

现在它适用于任何地方发布、调试、Azure、本地......

请忽略下面的所有内容...与解决问题无关。

找到我的解决方案...最终问题与代码无关。

我认为该问题与您创建应用程序时创建的默认应用程序设置有关 Azure App Service,但我同时做了几处更改。我在下面概述了我的所有更改。

配置 -> 常规设置

    平台:32 位到 64 位 始终开启:关闭到开启

配置 -> 应用程序设置

    我删除了在应用创建/首次发布期间添加的默认 6 项 我在拍摄此屏幕截图并通过 Azure 门户添加我的数据库和应用程序见解后返回

扩展:添加 ASP.NET Core 3.0 和 3.1 运行时

这些已经是我几天前所做的事情了,但以防我想确保我列出了我的所有自定义项

毕竟,我回到概述并在顶部栏上重新启动。然后我回到我的页面,没有错误!我还重新发布了几次,恢复了我所有的代码更改,它继续工作。

【讨论】:

以上是关于Blazor WebAssembly - 找不到适合类型“JwtSecurityTokenHandler”的构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 GitLab Pages 中将 Blazor WebAssembly 部署为静态站点

如何在 AWS S3/CDN 中托管具有环境特定值的 Blazor Webassembly

如何在 Blazor WebAssembly 中将 Json 结果转换为字符串?

初尝 Blazor WebAssembly

初尝 Blazor WebAssembly

明晚7点半 | Blazor + WebAssembly开启Web开发新体验