using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; using System.Threading; using System.Threading.Tasks; using Aitex.Common.Util; using Aitex.Core.Account; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.Util; using Aitex.Core.WCF; using MECF.Framework.Common.Account.Extends; using MECF.Framework.Common.Equipment; namespace MECF.Framework.Common.Account { public class AccountExManager : Singleton { #region Variables private readonly string _scAccountTemplateFile = PathManager.GetCfgDir() + "Account//Account.xml"; private readonly string _scAccountLocalFile = PathManager.GetCfgDir() + "Account//_AccountEx.xml"; // 旧的_Account.xml文件路径,为兼容已有的账户配置。 private readonly string _oldAccountXmlFile = PathManager.GetCfgDir() + "Account//_Account.xml"; private readonly object _syncRoot = new(); #endregion #region Constructors public AccountExManager() { } #endregion #region Properties /// /// 返回角色加载器。 /// public RoleLoader RoleLoader { get; private set; } #endregion #region Methods /// /// 初始化当前对象。 /// /// /// public void Initialize(bool enableService) { if (!File.Exists(_scAccountLocalFile)) { if (File.Exists(_oldAccountXmlFile)) { // 如果已存在_Account.xml,则从_Account.xml创建_AccountEx.xml文件。 File.Copy(_oldAccountXmlFile, _scAccountLocalFile); Thread.Sleep(10); } else if (File.Exists(_scAccountTemplateFile)) { File.Copy(_scAccountTemplateFile, _scAccountLocalFile); Thread.Sleep(10); } // 模板文件不存在 throw new ApplicationException("Can not initialize account configuration file, " + _scAccountTemplateFile); } // 默认不支持多用户登录。 CredentialManager.Instance.Initialize(false); RoleLoader = new RoleLoader(_scAccountLocalFile); RoleLoader.Load(); if (enableService) { Singleton.Instance.Initialize(new Type[1] { typeof(AccountService) }); } } private RemoteEndpointMessageProperty GetCurrentEndPoint() { var context = OperationContext.Current; var prop = context.IncomingMessageProperties; var endpoint = prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty; return endpoint; } private readonly Dictionary _dictLoginCts = new (); public async Task WaitUserLogout(Credential user, Credential newUser) { // 如果当前用户名有对应的CancellationTokenSource,并且没有触发取消,说明某个客户端正在使用当前用户发起登录请求,此时 // 不可重复请求登录。 if (_dictLoginCts.TryGetValue(user.AccountInfo.LoginName, out var cts) && cts is { IsCancellationRequested: false }) { // 该用户名正在请求登录,拒绝新的登录请求 EV.PostWarningLog(ModuleName.System.ToString(), "The user has been requesting login, please try again later."); } // 用户已登录,需要已登录的客户端确认退出后,再放行凭据 cts = new CancellationTokenSource(); _dictLoginCts.Add(user.AccountInfo.LoginName, cts); var ct = cts.Token; return await Task.Run(async () => { // 请求已登录的客户端确认是否同意注销 EV.PostLoginBySameUser(user); // 等待已登录的凭据被移除(客户端注销) var sw = new Stopwatch(); sw.Start(); while (true) { if (!CredentialManager.Instance.CheckHasLoggedInByToken(user.Token)) { // 客户端已注销,可以放行新的凭据 CredentialManager.Instance.Add(newUser); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, user.AccountInfo.LoginName); return await Task.FromResult(LoginRequestResults.Confirmed); } Thread.Sleep(1000); ct.ThrowIfCancellationRequested(); if (sw.Elapsed.TotalSeconds > 30) break; } // 等待客户端注销超时 _dictLoginCts.Remove(user.AccountInfo.LoginName); return LoginRequestResults.Timeout; }, ct); } public async Task RequestLogin(string loginName, string password, string role, LoginClientInfo clientInfo) { Task ret; if (!Monitor.Wait(_syncRoot, 40000)) throw new TimeoutException($"timeout to lock the credential manager."); var endPoint = GetCurrentEndPoint(); try { var accountList = RoleLoader.AccountList; var matchedAccount = accountList.FirstOrDefault(x => x.LoginName == loginName); if (matchedAccount == null) // 用户不存在 return await Task.FromResult(LoginRequestResults.NoMatchUser); if (matchedAccount.Password != password) // 密码错误 return await Task.FromResult(LoginRequestResults.WrongPwd); // 查找当前用户的登录角色是否存在 var matchedRole = matchedAccount.RoleIDs.FirstOrDefault(x => x == role); if (matchedRole == null) // 找不到角色 return await Task.FromResult(LoginRequestResults.NoMatchRole); // 创建新凭据 var newCred = new Credential(CredentialManager.GenerateToken(), matchedAccount) { LoginIP = endPoint.Address, LoginPort = endPoint.Port, LoginTime = DateTime.Now }; if(CredentialManager.Instance.Credentials.Count == 0) { // 没有用户登录,直接发放凭据 CredentialManager.Instance.Add(newCred); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, loginName); return await Task.FromResult(LoginRequestResults.Confirmed); } if (CredentialManager.Instance.IsSupportMultiUserLogin) { // 查找当前请求的用户是否已经登录 var loggedCred = CredentialManager.Instance.GetCredentialByUserName(loginName); // 支持多用户同时登录 if (loggedCred == null) { // 用户未登录,直接放行凭据 CredentialManager.Instance.Add(newCred); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, loginName); return await Task.FromResult(LoginRequestResults.Confirmed); } return await WaitUserLogout(loggedCred, newCred); } else { if (CredentialManager.Instance.Credentials.Count > 1) { EV.PostWarningLog(ModuleName.System.ToString(), $"{nameof(CredentialManager)} does not support multiple users login but more than one credentials found."); } var loggedCred = CredentialManager.Instance.Credentials.First().Value; return await WaitUserLogout(loggedCred, newCred); } } catch (Exception ex) { LOG.Error(ex.Message, ex); return await Task.FromResult(LoginRequestResults.None); } finally { _dictLoginCts.Remove(loginName); Monitor.Exit(_syncRoot); } } public void CancelLoginRequest(string userName) { if (_dictLoginCts.TryGetValue(userName, out var cts)) cts.Cancel(); } internal void Logout(string token) { lock (_syncRoot) { if (CredentialManager.Instance.Credentials.TryGetValue(token, out var cred)) { EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedOff, cred.AccountInfo.LoginName); CredentialManager.Instance.Remove(token); } } } #endregion } }