Identity Server 4 AddOidcStateDataFormatterCache配置TimeToLive

时间:2019-06-20 20:17:54

标签: identityserver4

我正在使用扩展名:

services.AddOidcStateDataFormatterCache();

在Asp.Net Core中,将状态存储在使用Redis实现的分布式缓存中:

services.AddStackExchangeRedisCache(options => {
                options.Configuration = Configuration[RedisConnection];
            });

,但似乎Redis缓存中的条目未设置为TTL: enter image description here

是否存在用于控制在缓存中创建的键的TTL的设置?

1 个答案:

答案 0 :(得分:1)

已经reported。等待回应。 (请注意,那里也需要这个!)

目前,我们使用一个丑陋的继承者。丑陋的原因是该库没有虚拟方法,并且还需要一个辅助程序internal class ConfigureOpenIdConnectOptionsTTL : IPostConfigureOptions<OpenIdConnectOptions>(主要是再次复制和粘贴),但是至少它可以解决“生产中的redis缓慢”问题。

public class DistributedCacheStateDataFormatterTTL: 
    DistributedCacheStateDataFormatter, ISecureDataFormat<AuthenticationProperties>
{
    public static readonly TimeSpan DefaultCacheDuration = TimeSpan.FromMinutes(5);
    private readonly IHttpContextAccessor _httpContext;
    private readonly string _name;

    public DistributedCacheStateDataFormatterTTL(
        IHttpContextAccessor httpContext, string name) : base(httpContext, name)
    {
        _httpContext = httpContext;
        _name = name;
    }
    private string CacheKeyPrefix => "DistributedCacheStateDataFormatter";

    private IDistributedCache Cache =>
        _httpContext.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
    private IDataProtector Protector =>
       _httpContext.HttpContext.RequestServices
       .GetRequiredService<IDataProtectionProvider>()
       .CreateProtector(CacheKeyPrefix, _name);

    string ISecureDataFormat<AuthenticationProperties>
        .Protect(AuthenticationProperties data)
    {
         return ((ISecureDataFormat<AuthenticationProperties>)this).
             Protect(data, string.Empty);
    }


    string ISecureDataFormat<AuthenticationProperties>
        .Protect(AuthenticationProperties data, string purpose)
    {
         var key = Guid.NewGuid().ToString();
         var cacheKey = $"{CacheKeyPrefix}-{purpose}-{key}";
         var json = JsonConvert.SerializeObject(data, new JsonSerializerSettings()
         {
             DefaultValueHandling = DefaultValueHandling.Ignore,
             NullValueHandling = NullValueHandling.Ignore
         });

         var options = new DistributedCacheEntryOptions();
         if (data.ExpiresUtc.HasValue)
             options.SetAbsoluteExpiration(data.ExpiresUtc.Value);
         else
             options.SetSlidingExpiration(DefaultCacheDuration);

         // Rather than encrypt the full AuthenticationProperties
         // cache the data and encrypt the key that points to the data
         Cache.SetString(cacheKey, json, options);

         return Protector.Protect(key);
    }
}
internal class ConfigureOpenIdConnectOptionsTTL : IPostConfigureOptions<OpenIdConnectOptions>
{
    private string[] _schemes;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ConfigureOpenIdConnectOptionsTTL(string[] schemes, IHttpContextAccessor httpContextAccessor)
    {
        _schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    }

    public void PostConfigure(string name, OpenIdConnectOptions options)
    {
        // no schemes means configure them all
        if (_schemes.Length == 0 || _schemes.Contains(name))
        {
            options.StateDataFormat = new DistributedCacheStateDataFormatterTTL(_httpContextAccessor, name);
        }
    }


    public static IServiceCollection AddOidcStateDataFormatterCache(
        IServiceCollection services,
        params string[] schemes)
    {
        services.RemoveAll<IPostConfigureOptions<OpenIdConnectOptions>>();
        services.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>>(
            svcs => new ConfigureOpenIdConnectOptionsTTL(
                    schemes,
                    svcs.GetRequiredService<IHttpContextAccessor>())
            );

        return services;
    }
}