ASP.NET Core 5 JwtBearer 基于用户的动态秘密

Mar*_*tin 5 c# jwt asp.net-core-5.0

我正在尝试使用 JWT 身份验证创建简单的 API,其中每个用户都有不同的密钥,而不是一个全局密钥。这听起来是个简单的问题,但棘手的问题是如何在 StartUp 中正确获取 UserManager进行验证。

第一个也是显而易见的方法是通过依赖注入,如下所示:

public IConfiguration Configuration { get; }
public UserManager<IdentityUser> UserManager { get; }

public Startup(IConfiguration configuration, UserManager<IdentityUser> userManager)
{
        Configuration = configuration;
        UserManager = userManager;
}       
Run Code Online (Sandbox Code Playgroud)

但这会立即崩溃并出现错误:“ System.InvalidOperationException:'无法解析类型'Microsoft.AspNetCore.Identity.UserManager`1 的服务

所以第二次尝试是使用 BuildServiceProvider:

public void ConfigureServices(IServiceCollection services)
{
       ...
       UserManager<IdentityUser> userManager = services.BuildServiceProvider().GetService<UserManager<IdentityUser>>();
       ...
}
Run Code Online (Sandbox Code Playgroud)

这实际上是有效的,但有一个丑陋的警告:“ ASP0000:从应用程序代码调用‘BuildServiceProvider’会导致创建单例服务的附加副本。请考虑替代方案,例如依赖注入服务作为‘配置’的参数。

一段时间后,我想出了第三个最终解决方案:

private IApplicationBuilder _app;

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
       ...
       _app = app;
}

public void ConfigureServices(IServiceCollection services)
{
       ...
       services.AddAuthentication().AddJwtBearer(options => {
           options.TokenValidationParameters = new TokenValidationParameters()
           {
               ValidateIssuer = true,
               ValidateAudience = true,
               ValidateLifetime = true,
               ValidIssuer = Configuration["JWT:Issuer"],
               ValidAudience = Configuration["JWT:Audience"],
               IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
               {
                   List<SecurityKey> keys = new List<SecurityKey>();

                   JwtSecurityToken jwtToken = (JwtSecurityToken)securityToken;
                   Claim claimName = jwtToken.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name);
                   if (claimName != null)
                   {
                       string name = claimName.Value;
                       using IServiceScope serviceScope = _app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope();
                       UserManager<IdentityUser> userManager = serviceScope.ServiceProvider.GetService<UserManager<IdentityUser>>();
                       IdentityUser user = userManager.FindByNameAsync(name).GetAwaiter().GetResult();

                       if (user != null) keys.Add(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(user.JwtSecret)));
                   }

                   return keys;
               },
               //IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"])),
               ClockSkew = TimeSpan.Zero
           };
       });
}
Run Code Online (Sandbox Code Playgroud)

该解决方案有效,到目前为止没有警告或错误,但它干净吗?我可以改进它还是应该采取我没有想到的不同方法?不要对我这么严厉 - 我对 ASP 来说是个新手;)

如果您到目前为止已经做到了并且/或者会给我任何提示,我将不胜感激。谢谢。