208 lines
5.5 KiB
C#
208 lines
5.5 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using Aitex.Core.RT.Event;
|
|||
|
using Aitex.Core.Util;
|
|||
|
|
|||
|
namespace Aitex.Core.Account;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 登录凭据管理器。
|
|||
|
/// </summary>
|
|||
|
public class CredentialManager : Singleton<CredentialManager>
|
|||
|
{
|
|||
|
#region Variables
|
|||
|
|
|||
|
private readonly object _syncRoot = new();
|
|||
|
|
|||
|
private readonly Dictionary<string, Credential> _dictLoginCredentials = new ();
|
|||
|
private readonly PeriodicJob _threadMonitorCred;
|
|||
|
private bool _isInitialized;
|
|||
|
private int _maxCredentialAllowed;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Constructors
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 登录凭据管理的构造函数。
|
|||
|
/// </summary>
|
|||
|
public CredentialManager()
|
|||
|
{
|
|||
|
_threadMonitorCred = new(1000, OnTimer, "CredentialAliveMonitorThread", true, true);
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Properties
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回当前凭据管理器是否支持多用户登录。
|
|||
|
/// </summary>
|
|||
|
public bool IsSupportMultiUserLogin => _maxCredentialAllowed > 1;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回已登录的凭据。
|
|||
|
/// </summary>
|
|||
|
public IReadOnlyDictionary<string, Credential> Credentials => _dictLoginCredentials;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 初始化登录凭据管理器。
|
|||
|
/// </summary>
|
|||
|
/// <param name="isSupportMultiUsersLogin">是否支持多用户同时登录。</param>
|
|||
|
public void Initialize(bool isSupportMultiUsersLogin)
|
|||
|
{
|
|||
|
if (_isInitialized)
|
|||
|
throw new InvalidOperationException($"{nameof(CredentialManager)} has been initialized.");
|
|||
|
|
|||
|
_isInitialized = true;
|
|||
|
_maxCredentialAllowed = isSupportMultiUsersLogin ? int.MaxValue : 1;
|
|||
|
}
|
|||
|
|
|||
|
private bool OnTimer()
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
foreach (var kvp in _dictLoginCredentials)
|
|||
|
{
|
|||
|
var cred = kvp.Value;
|
|||
|
if ((DateTime.Now - cred.LastAliveTime).TotalSeconds > 60)
|
|||
|
{
|
|||
|
// 如果当前凭据超过60s未激活一次,表示客户端可能已经离线,移除
|
|||
|
cred.State = CredentialState.Expired;
|
|||
|
EV.PostLoginBySameUser(cred);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 检查指定的用户名是否已经登录。
|
|||
|
/// </summary>
|
|||
|
/// <param name="userName"></param>
|
|||
|
/// <returns></returns>
|
|||
|
internal bool CheckHasLoggedIn(string userName)
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
return _dictLoginCredentials.Values.FirstOrDefault(x => x.AccountInfo.LoginName == userName) != null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 检查指定令牌的登录凭据是否存在。
|
|||
|
/// </summary>
|
|||
|
/// <param name="token"></param>
|
|||
|
/// <returns></returns>
|
|||
|
internal bool CheckHasLoggedInByToken(string token)
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
return _dictLoginCredentials.ContainsKey(token);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 报告客户端处于活动状态。
|
|||
|
/// </summary>
|
|||
|
/// <param name="token">客户端登录凭据令牌。</param>
|
|||
|
/// <returns></returns>
|
|||
|
public CredentialKeepAliveResults KeepAlive(string token)
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
if (_dictLoginCredentials.TryGetValue(token, out var cred))
|
|||
|
{
|
|||
|
if (cred.State == CredentialState.Expired)
|
|||
|
return CredentialKeepAliveResults.Expired;
|
|||
|
cred.LastAliveTime = DateTime.Now; // 刷新时间
|
|||
|
return CredentialKeepAliveResults.Alive;
|
|||
|
}
|
|||
|
|
|||
|
return CredentialKeepAliveResults.NotFound;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 将指定的凭据加入字典。
|
|||
|
/// </summary>
|
|||
|
/// <param name="cred"></param>
|
|||
|
/// <exception cref="Exception"></exception>
|
|||
|
public void Add(Credential cred)
|
|||
|
{
|
|||
|
if (CheckHasLoggedIn(cred.AccountInfo.LoginName))
|
|||
|
throw new Exception($"user {cred.AccountInfo.LoginName} has been logged in.");
|
|||
|
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
if (_dictLoginCredentials.Count >= _maxCredentialAllowed)
|
|||
|
throw new InvalidOperationException("maximum number of login credentials reached");
|
|||
|
|
|||
|
_dictLoginCredentials[cred.Token] = cred;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 移除指定令牌的凭据。
|
|||
|
/// </summary>
|
|||
|
/// <param name="token"></param>
|
|||
|
public void Remove(string token)
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
_dictLoginCredentials.Remove(token);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取指定用户名的登录凭据。
|
|||
|
/// </summary>
|
|||
|
/// <param name="userName"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public Credential GetCredentialByUserName(string userName)
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
return _dictLoginCredentials.Values.FirstOrDefault(x => x.AccountInfo.LoginName == userName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 校验指定令牌的凭据是否有效。
|
|||
|
/// </summary>
|
|||
|
/// <param name="token"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public bool ValidateCredential(string token)
|
|||
|
{
|
|||
|
lock (_syncRoot)
|
|||
|
{
|
|||
|
if (_dictLoginCredentials.TryGetValue(token, out var cred))
|
|||
|
{
|
|||
|
return cred.State == CredentialState.Alive;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Static Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 创建一个令牌。
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public static string GenerateToken()
|
|||
|
{
|
|||
|
return Guid.NewGuid().ToString("N");
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|