Jwt Bearer和依赖注入

12

我正在尝试配置我的Jwt Bearer发行者密钥,但通常在生产中,我使用由KeyManager包装的Azure Key Vault。

KeyManager类已在依赖注入中进行了配置,但是在ConfigureServices方法中我无法使用它(显然),但如果我不能使用它,我将无法检索到我的密钥。

目前我的解决方案是构建一个临时服务提供程序并使用它,但我认为这不是最先进的技术(而且我需要创建单例的两个副本,不太好)。

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    ServiceProvider sp = services.BuildServiceProvider();
    IKeyManager keyManager = sp.GetService<KeyManager>();

    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = keyManager.GetSecurityKeyFromName("jwt").Result,

        ValidIssuer = "https://api.example.com",
        ValidateIssuer = true
    };

    options.Audience = "https://api.example.com";
    options.Authority = "https://api.example.com";

    options.SaveToken = true;
});

我相信 KeyManager 是你自己的实现,你对它有完全的控制权。由于 BuildServiceProvider 是一个昂贵的操作,你可以考虑将 KeyManager 的功能转换为扩展方法。另外,keyManager.GetSecurityKeyFromName("jwt").Result 是一个异步方法吗?如果是这样,那么使用 .Result 的方式是不正确的。 - Kosala W
2个回答

27

使用 Options pattern 并实现 IConfigureNamedOptions<JwtBearerOptions>

public class ConfigureJwtBearerOptions : IConfigureNamedOptions<JwtBearerOptions>
{
    private readonly IKeyManager _keyManager;

    public ConfigureJwtBearerOptions(IKeyManager keyManager)
    {
        _keyManager = keyManager;
    }

    public void Configure(JwtBearerOptions options)
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = _keyManager.GetSecurityKeyFromName("jwt").Result,

            ValidIssuer = "https://api.example.com",
            ValidateIssuer = true
        };

        options.Audience = "https://api.example.com";
        options.Authority = "https://api.example.com";

        options.SaveToken = true;
    }

    public void Configure(string name, JwtBearerOptions options)
    {
        Configure(options);
    }
}

Startup.cs文件中:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer();

services.ConfigureOptions<ConfigureJwtBearerOptions>();

1
非常感谢您的回答。这展示了 ASP.NET 设计之美。 - canvee

11

因此,在进行更多研究后,我发现了 Microsoft 文档中的此页面:使用 DI 服务配置选项(还请参考此答案,该答案涉及处理动态多个 Jwt Issuer)。

services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
.Configure<IKeyManager>((options, keyManager) => {

    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = keyManager.GetSecurityKeyFromName("jwt").Result,

        ValidIssuer = "https://api.example.com",
        ValidateIssuer = true
    };

    options.Audience = "https://api.example.com";
    options.Authority = "https://api.example.com";

    options.SaveToken = true;
});

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer();

1
是的,这也可以。我会将配置代码提取到单独的类中,以保持 Startup.cs 更加清洁 :) - weichch
这绝对是最好的答案,因为意识到这一点可以让您配置任何服务选项,例如ConfigureApplicationCookie是一个简单的services.AddOptions<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme).Configure<MyDependency>( - Angel Yordanov
我已经很长时间在寻找解决我的“签名密钥生命周期问题”的方法。(尽管我的密钥来自不同的来源,而我的主要问题是在正确的时间处置它。)你的解决方案非常优雅,对我很有效。(已在.NET 6上测试。)谢谢! - Carsten Führmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接