c# - 如何在 Redis 中创建持久票证

标签 c# asp.net-core redis identityserver4

我有一个 Identity Server 4 应用程序,我试图记住(持久化)用户登录。

我想我已经将问题归结为 Redis 中的某些内容。

当门票存储在 redis 中时,它们会在存储时超时。

127.0.0.1:6379> ttl Ids-v2-Local-Key-74c112d5-e0f4-48c4-9d0f-cd8e62f12dfd
(integer) 3014

只要应用打开redis就会刷新超时。

1542808250.760394 [0 lua] "EXPIRE" "Ids-v2-Local-Key-74c112d5-e0f4-48c4-9d0f-cd8e62f12dfd" "3600"

只要用户在他们键入的应用程序上处于事件状态,就可以继续刷新。但是,如果用户回家并在第二天回来,他们的应用程序将不再登录。

我能够通过手动登录到 redis 并将 key 设置为 Persist 来解决此问题

127.0.0.1:6379> Persist XenaIdentityserver-v2-Local-Key-74c112d5-e0f4-48c4-9d0f-cd8e62f12dfd
(integer) 1
127.0.0.1:6379> ttl XenaIdentityserver-v2-Local-Key-74c112d5-e0f4-48c4-9d0f-cd8e62f12dfd
(integer) -1

我认为问题在于如何在 Redis 中创建键。

RedisCacheTicketStore

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Redis;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;

namespace Xena.IdentityServer.Services
{
    public class RedisCacheTicketStore : ITicketStore
    {
        private readonly ILogger _logger;
        private string KeyPrefix = "AuthSessionStore-";
        private IDistributedCache _cache;

        public RedisCacheTicketStore(RedisCacheOptions options, ILogger logger, IConfiguration config)
        {
            KeyPrefix = config["Redis:ApplicationName"] + "-";

            _logger = logger;
            _cache = new RedisCache(options);
        }

        public async Task<string> StoreAsync(AuthenticationTicket ticket)
        {
            var sw = new Stopwatch();
            sw.Start();

            var guid = Guid.NewGuid();
            var key = KeyPrefix + guid.ToString();
            await RenewAsync(key, ticket);

            _logger.LogDebug(LoggingEvents.RedisCacheTicketStore, "Redis Method StoreAsync Elapsed {sw.ElapsedMilliseconds}", sw.ElapsedMilliseconds);

            return key;
        }

        public Task RenewAsync(string key, AuthenticationTicket ticket)
        {
            var sw = new Stopwatch();
            sw.Start();

            var options = new DistributedCacheEntryOptions();
            var expiresUtc = ticket.Properties.ExpiresUtc;
            if (expiresUtc.HasValue)
            {
                options.SetAbsoluteExpiration(expiresUtc.Value);
            }

            options.SetSlidingExpiration(TimeSpan.FromMinutes(60));

            byte[] val = SerializeToBytes(ticket, _logger);
            _cache.Set(key, val, options);
            sw.Stop();
            _logger.LogDebug(LoggingEvents.RedisCacheTicketStore, "Redis Method RenewAsync Elapsed {sw.ElapsedMilliseconds}", sw.ElapsedMilliseconds);
            return Task.FromResult(0);
        }

        public Task<AuthenticationTicket> RetrieveAsync(string key)
        {
            var sw = new Stopwatch();
            sw.Start();

            AuthenticationTicket ticket;
            byte[] bytes = null;
            bytes = _cache.Get(key);
            ticket = DeserializeFromBytes(bytes, _logger);

            sw.Stop();
            _logger.LogDebug(LoggingEvents.RedisCacheTicketStore, "Redis Method RetrieveAsync Elapsed {sw.ElapsedMilliseconds}", sw.ElapsedMilliseconds);

            return Task.FromResult(ticket);
        }

        public Task RemoveAsync(string key)
        {
            var sw = new Stopwatch();
            sw.Start();

            _cache.Remove(key);

            sw.Stop();
            _logger.LogDebug(LoggingEvents.RedisCacheTicketStore, "Redis Method RemoveAsync Elapsed {sw.ElapsedMilliseconds}", sw.ElapsedMilliseconds);


            return Task.FromResult(0);
        }

        private static byte[] SerializeToBytes(AuthenticationTicket source, ILogger logger)
        {
            var sw = new Stopwatch();
            sw.Start();

            var ticket = TicketSerializer.Default.Serialize(source);

            sw.Stop();
            logger.LogDebug(LoggingEvents.RedisCacheTicketStore, "Redis Method SerializeToBytes Elapsed {sw.ElapsedMilliseconds}", sw.ElapsedMilliseconds);

            return ticket;
        }

        private static AuthenticationTicket DeserializeFromBytes(byte[] source, ILogger logger)
        {
            var sw = new Stopwatch();
            sw.Start();

            var hold = source == null ? null : TicketSerializer.Default.Deserialize(source);

            sw.Stop();
            logger.LogDebug(LoggingEvents.RedisCacheTicketStore, "Redis Method DeserializeFromBytes Elapsed {sw.ElapsedMilliseconds}", sw.ElapsedMilliseconds);

            return hold;
        }   

    }
}

我可以查看代码并看到在执行 StoreAsync 方法时 AuthenticationTicket 设置为 isPersistant。但是,它不会创建持久性票证,它仍然有超时。

enter image description here

我如何告诉 _cache.Set(key, val, options);设置持久票而不是超时票?

最佳答案

来自 Kirk Larkin 在评论中的提示。我突然想到,我真正需要做的就是将超时设置得更长,然后只要用户处于事件状态,它就会继续滑动。如果我将它设置为 Persist,那么它将永远存在于 Redis 中,而这并不是我们想要的。我们希望它在给定的时间段内存在,只要用户不时回来,他们就会继续显示为已登录。

public Task RenewAsync(string key, AuthenticationTicket ticket)
        {
            var options = new DistributedCacheEntryOptions();
            var expiresUtc = ticket.Properties.ExpiresUtc;
            if (expiresUtc.HasValue)
                options.SetAbsoluteExpiration(expiresUtc.Value);

            if (ticket.Properties.IsPersistent && !expiresUtc.HasValue)
                options.SetSlidingExpiration(_rememberMeTimeoutInDays);
            else if (ticket.Properties.IsPersistent && expiresUtc.HasValue)
                options.SetSlidingExpiration(TimeSpan.FromTicks(expiresUtc.Value.Ticks));
            else
                options.SetSlidingExpiration(_defaultTimeoutInHours);

            byte[] val = SerializeToBytes(ticket, _logger);
            _cache.Set(key, val, options);
            return Task.FromResult(0);
        }

关于c# - 如何在 Redis 中创建持久票证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53413862/

相关文章:

c# - 在没有获得焦点的情况下打开隐藏的 Internet Explorer 窗口?

c# - 为派生的Model类创建不同的ViewModel对象

c# - Azure blob 上传在 .Net Core 3.0 应用程序中调用 .Upload() 时挂起

c# - 使用 EmguCV (OpenCV) 求解共面点位姿估计

c# - C# 和 CPython 之间快速且可扩展的 RPC

azure-web-app-service - Azure 上的 Asp.Net VNext 应用程序设置

visual-studio-2015 - 在 Azure 应用服务上远程调试 ASP.NET Core 1.0 应用程序

javascript - 从客户端设置和清除 Node 服务器上的超时

php - Redis可用性检查

Laravel 存储 session 到 redis