所以我一直在尝试以 JWT 分发的形式设置一个带有 token 身份验证的小型测试 api, token 分发部分按预期工作。

但是,由于我想让 JWT 服务的方法更通用以允许对 token 进行不同类型的签名(因为我更喜欢私钥/公钥对),因此我尝试在 appsettings 文件中设置更多选项,然后决定了 token 的生成方式,我开始在这些设置中加载依赖注入(inject),直到现在我几乎没有接触过。

因此,当我想采用我设置为单例的那些配置类时,问题就出现了(到目前为止,我阅读的大多数指南都已经完成了这一点,所以我认为它有点正确)并在 ConfigureServices 方法中使用它们他们添加了它,以便我可以使用在我看来应该设置的参数,因为我通过获取 appsettings 文件的一部分配置了上面的几行。

然而,一旦我尝试访问它们,我就什么也得不到,而是剩下空值。

启动文件

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.
    public void ConfigureServices(IServiceCollection services)
    {
        //the token config takes values from the appsettings.json file
        var tokenConf = Configuration.GetSection("TokenConfiguration");
        services.Configure<TokenConfiguration>(tokenConf);

        //the signing credentials are assigned in the JwtTokenService constructor
        var signingConf = new SigningConfiguration();
        services.AddSingleton<SigningConfiguration>(signingConf);
        //my token service
        services.AddSingleton<IJwtTokenService, JwtTokenService>();

        //i try to get hold of the actual values to use later on
        var provider = services.BuildServiceProvider();
        TokenConfiguration tc = provider.GetService<TokenConfiguration>();
        SigningConfiguration sc = provider.GetService<SigningConfiguration>();

        //i wanna use the values in here when i set the parameters for my authentication
        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(x =>
        {
            x.Events = new JwtBearerEvents
            {
                OnTokenValidated = context =>
                {
                    return Task.CompletedTask;
                }
            };

            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                //values used here since i specify issuer, audience and what kind of key to use in the settings
                //the key & credentials differ based on a bool in the settings file and will either be a symmetric or asymmetric key
                ValidIssuer = tc.Issuer,
                ValidAudience = tc.Audience,
                IssuerSigningKey = sc.Key
            };
        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

JwtTokenService.cs(IJwtTokenService只有这里实现的CreateToken方法)
public class JwtTokenService : IJwtTokenService
{
    private TokenConfiguration tokenConf;
    public SigningConfiguration signingConf;

    public JwtTokenService(IOptions<TokenConfiguration> tc) {
        tokenConf = tc.Value;
        signingConf = new SigningConfiguration();

        //if the asymmetric bool is set to true, assign a new rsa keypair to the signing configuration
        //otherwise, use a symmetric key with a hmac hash
        if (tc.Value.AsymmetricKey)
        {
            using (var provider = new RSACryptoServiceProvider(2048))
            {
                signingConf.Key = new RsaSecurityKey(provider.ExportParameters(true));
            }

            signingConf.SigningCredentials =
                new SigningCredentials(
                    signingConf.Key,
                    SecurityAlgorithms.RsaSha256);
        }
        else {
            signingConf.Key =
                new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(tc.Value.HmacSecret));

            signingConf.SigningCredentials =
                new SigningCredentials(
                    signingConf.Key,
                    SecurityAlgorithms.HmacSha512);
        }
    }

    /// <summary>
    /// Creates a token based on the running configuration
    /// </summary>
    public string CreateToken(List<Claim> claims)
    {
        var token = new JwtSecurityToken(
            issuer: tokenConf.Issuer,
            audience: tokenConf.Audience,
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(tokenConf.Minutes),
            signingCredentials: signingConf.SigningCredentials
            );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

token 配置文件
public class TokenConfiguration
{
    public string Audience { get; set; }
    public string Issuer { get; set; }
    public int Minutes { get; set; }
    public bool AsymmetricKey { get; set; }
    public string HmacSecret { get; set; }
}

签名配置文件
public class SigningConfiguration
{
    public SecurityKey Key { get; set; }
    public SigningCredentials SigningCredentials { get; set; }
}

appsettings.json
"TokenConfiguration": {
"Audience": "ExampleAudience",
"Issuer": "ExampleIssuer",
"Minutes": 30,
"AsymmetricKey": true,
"HmacSecret": "example-secret-top-secret-secret-is_secret"

}

(该项目在 asp.net core 2.1 中运行,以防万一)

我是 DI 新手,找不到很多用例与我的用例相同的示例,而且大多数此类案例都涉及实际服务,而不是通过 DI 添加“配置”类。

可能有一种更好的方法来做到这一点,我可能只是愚蠢,因为没有注意到或不知道谷歌确切的东西来获得正确的答案,无论如何,很可能会在此之后观看/阅读一些关于 DI 的内容.

任何输入或想法都将受到高度赞赏,因为我对 asp.net 核心和整个流程仍然不熟悉。

作为关于私钥生成的一个小问题,在我的情况下,最好将生成的 key 对存储在 keystore 中,或将其存储在内存中,还是通过诸如 openSSL 之类的东西生成它们并在启动时读取它们是最好的选择?

最佳答案

您正在从 ServiceProvider 请求 TokenConfiguration 而不是 IOptions<TokenConfiguration>
更改此行

   TokenConfiguration tc = provider.GetService<TokenConfiguration>();
   SigningConfiguration sc = provider.GetService<SigningConfiguration>();


    IOptions<TokenConfiguration> tc = provider.GetService<IOptions<TokenConfiguration>>();
    IOptions<SigningConfiguration> sc = provider.GetService<IOptions<SigningConfiguration>>();

然后使用 tc.Value 访问选项。

ServiceProvider 中构建 ConfigureServices 不是一个好主意,我会在 Configuration["TokenConfiguration:Audience"] 中需要的任何地方直接从 ConfigureServices 配置中获取。

关于c# - 使用相同的 ConfigureServices 方法访问在 asp.net core 的 startup.cs 文件中的 ConfigureServices 中创建的单例,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53707133/

10-12 05:54