我有一个 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。但是,它不会创建持久性票证,它仍然有超时。
我如何告诉 _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/