You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OnlineMsgServer/Core/UserService.cs

282 lines
8.7 KiB
C#

using OnlineMsgServer.Common;
namespace OnlineMsgServer.Core
{
class UserService
{
#region 服务器用户管理
private static readonly List<User> _UserList = [];
private static readonly object _UserListLock = new();
/// <summary>
/// 通过wsid添加用户记录
/// </summary>
public static void AddUserConnect(string wsid, string ipAddress, string challenge)
{
lock (_UserListLock)
{
User user = new(wsid);
user.IpAddress = ipAddress;
user.PendingChallenge = challenge;
user.ChallengeIssuedAtUtc = DateTime.UtcNow;
_UserList.Add(user);
}
}
/// <summary>
/// 通过wsid移除用户记录
/// </summary>
/// <param name="wsid"></param>
public static void RemoveUserConnectByID(string wsid)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
if (user != null)
{
_UserList.Remove(user);
}
}
}
/// <summary>
/// 通过publickey返回用户列表
/// </summary>
public static List<User> GetUserListByPublicKey(string publicKey, bool includePeerNodes = true)
{
lock (_UserListLock)
{
return _UserList.FindAll(u =>
u.PublicKey == publicKey &&
u.IsAuthenticated &&
(includePeerNodes || !u.IsPeerNode));
}
}
/// <summary>
/// 通过wsid设置用户PublicKey
/// </summary>
public static void UserLogin(string wsid, string publickey, string name, bool isPeerNode = false)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
if (user != null)
{
user.PublicKey = publickey.Trim();
user.Name = name.Trim();
user.IsPeerNode = isPeerNode;
user.IsAuthenticated = true;
user.PendingChallenge = null;
user.AuthenticatedAtUtc = DateTime.UtcNow;
Console.WriteLine(user.ID + " 登记成功");
}
else
{
throw new Exception("用户不存在");
}
}
}
/// <summary>
/// 通过wsid获取用户PublicKey
/// </summary>
public static string? GetUserPublicKeyByID(string wsid)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
if (user is { IsAuthenticated: true })
{
return user.PublicKey;
}
return null;
}
}
/// <summary>
/// 通过wsid获取UserName
/// </summary>
public static string? GetUserNameByID(string wsid)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
if (user is { IsAuthenticated: true })
{
return user.Name;
}
return null;
}
}
/// <summary>
/// 通过用户PublicKey获取wsid
/// </summary>
public static string? GetUserIDByPublicKey(string publicKey)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.PublicKey == publicKey && u.IsAuthenticated);
if (user != null)
{
return user.ID;
}
return null;
}
}
public static bool IsAuthenticated(string wsid)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
return user is { IsAuthenticated: true };
}
}
public static bool IsPeerNodeSession(string wsid)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
return user is { IsAuthenticated: true, IsPeerNode: true };
}
}
public static string? GetPeerPublicKeyBySessionId(string wsid)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
if (user is { IsAuthenticated: true, IsPeerNode: true })
{
return user.PublicKey;
}
return null;
}
}
public static List<User> GetAuthenticatedUsers(bool includePeerNodes = true)
{
lock (_UserListLock)
{
return _UserList
.Where(u => u.IsAuthenticated && (includePeerNodes || !u.IsPeerNode))
.Select(u => new User(u.ID)
{
Name = u.Name,
PublicKey = u.PublicKey,
IsAuthenticated = u.IsAuthenticated,
IsPeerNode = u.IsPeerNode,
IpAddress = u.IpAddress,
PendingChallenge = u.PendingChallenge,
ChallengeIssuedAtUtc = u.ChallengeIssuedAtUtc,
AuthenticatedAtUtc = u.AuthenticatedAtUtc
})
.ToList();
}
}
public static int GetConnectionCount()
{
lock (_UserListLock)
{
return _UserList.Count;
}
}
public static bool TryGetChallenge(string wsid, out string challenge, out DateTime issuedAtUtc)
{
lock (_UserListLock)
{
challenge = "";
issuedAtUtc = DateTime.MinValue;
User? user = _UserList.Find(u => u.ID == wsid);
if (user == null || string.IsNullOrWhiteSpace(user.PendingChallenge))
{
return false;
}
challenge = user.PendingChallenge;
issuedAtUtc = user.ChallengeIssuedAtUtc;
return true;
}
}
public static bool TryRecordNonce(string wsid, string nonce, long timestamp, int replayWindowSeconds, out string reason)
{
lock (_UserListLock)
{
reason = "";
User? user = _UserList.Find(u => u.ID == wsid);
if (user == null)
{
reason = "user not found";
return false;
}
if (string.IsNullOrWhiteSpace(nonce))
{
reason = "nonce empty";
return false;
}
long cutoff = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - replayWindowSeconds;
List<string> expiredKeys = [];
foreach (KeyValuePair<string, long> item in user.ReplayNonceStore)
{
if (item.Value < cutoff)
{
expiredKeys.Add(item.Key);
}
}
foreach (string key in expiredKeys)
{
user.ReplayNonceStore.Remove(key);
}
if (user.ReplayNonceStore.ContainsKey(nonce))
{
reason = "replay nonce detected";
return false;
}
user.ReplayNonceStore[nonce] = timestamp;
return true;
}
}
public static bool IsRateLimitExceeded(string wsid, int maxRequests, int windowSeconds)
{
lock (_UserListLock)
{
User? user = _UserList.Find(u => u.ID == wsid);
if (user == null)
{
return true;
}
long nowMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
long cutoff = nowMs - (windowSeconds * 1000L);
while (user.RequestTimesMs.Count > 0 && user.RequestTimesMs.Peek() < cutoff)
{
user.RequestTimesMs.Dequeue();
}
if (user.RequestTimesMs.Count >= maxRequests)
{
return true;
}
user.RequestTimesMs.Enqueue(nowMs);
return false;
}
}
#endregion
}
}