81 lines
2.0 KiB
C#
81 lines
2.0 KiB
C#
using Microsoft.Extensions.Caching.Memory;
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace StoreHub.Framework.Security;
|
|
|
|
/// <summary>
|
|
/// 基于 <see cref="IMemoryCache"/> 的在线用户会话存储。
|
|
/// </summary>
|
|
public class OnlineUserStore : IOnlineUserStore
|
|
{
|
|
private const string CacheKeyPrefix = "online:user:";
|
|
private readonly IMemoryCache _cache;
|
|
// 在线人员索引:IMemoryCache 不提供公共枚举 API,这里用字典维护便于查询在线列表
|
|
private readonly ConcurrentDictionary<int, OnlineUserInfo> _onlineIndex = new();
|
|
|
|
public OnlineUserStore(IMemoryCache cache)
|
|
{
|
|
_cache = cache;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SignIn(OnlineUserInfo info)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(info);
|
|
|
|
// 以 userId 为键覆盖写入:旧会话(jti)被顶替,实现单点登录
|
|
_cache.Set(GetKey(info.UserId), info, new MemoryCacheEntryOptions
|
|
{
|
|
// 缓存条目与令牌同时过期,过期后自动失效
|
|
AbsoluteExpiration = info.ExpiresAt,
|
|
});
|
|
_onlineIndex[info.UserId] = info;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool IsTokenActive(int userId, string jti)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(jti))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return _cache.TryGetValue(GetKey(userId), out OnlineUserInfo? info)
|
|
&& info is not null
|
|
&& string.Equals(info.Jti, jti, StringComparison.Ordinal);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void SignOut(int userId)
|
|
{
|
|
_cache.Remove(GetKey(userId));
|
|
_onlineIndex.TryRemove(userId, out _);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<OnlineUserInfo> GetOnlineUsers()
|
|
{
|
|
DateTime now = DateTime.Now;
|
|
List<OnlineUserInfo> result = [];
|
|
foreach (KeyValuePair<int, OnlineUserInfo> pair in _onlineIndex)
|
|
{
|
|
if (pair.Value.ExpiresAt > now)
|
|
{
|
|
result.Add(pair.Value);
|
|
}
|
|
else
|
|
{
|
|
_onlineIndex.TryRemove(pair.Key, out _);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int Count => GetOnlineUsers().Count;
|
|
|
|
private static string GetKey(int userId) => $"{CacheKeyPrefix}{userId}";
|
|
}
|
|
|