using System; 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 SemaphoreSlim _syncRoot = new SemaphoreSlim(1, 1); #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) }); } } /// /// 获取WCF客户端IP信息。 /// /// private static RemoteEndpointMessageProperty GetCurrentWCFClientEndPoint() { var context = OperationContext.Current; var prop = context.IncomingMessageProperties; var endpoint = prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty; return endpoint; } /// /// 等待已登录的用户确认是否允许新的登录请求。 /// /// 已登录用户的凭据。 /// 新用户的登录凭据。 /// private static async Task RequestAndWaitLoginConfirmation(Credential userLoggedIn, Credential userRequesting) { // 等待已登录用户响应,返回结果; // 1. Rejected,拒绝新的登录请求 // 2. Logged Out,主动注销,允许新的登录请求 // 3. Timeout,超时没有响应,保持原用户的登录凭据 return await Task.Run(async () => { // 通知已登录的用户,其它客户端使用该用户名登录,是否允许登录 var ct = userRequesting.LoginRequestCancellationTokenSource.Token; // 等待已登录的凭据被移除(客户端注销) var sw = new Stopwatch(); sw.Start(); while (true) { switch (userRequesting.State) { case CredentialState.Confirmed: // 客户端已注销,可以放行新的凭据 CredentialManager.Instance.Grant(userRequesting); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, userLoggedIn.AccountInfo.LoginName); return await Task.FromResult(LoginRequestResults.Confirmed); case CredentialState.Reject or CredentialState.RequestCanceled: if(userRequesting.State == CredentialState.Reject) // 已登录用户拒绝了登录请求 EV.PostInfoLog(ModuleName.System.ToString(), $"User {userRequesting.AccountInfo.LoginName}'s login request from {userRequesting.LoginIP}:{userRequesting.LoginPort} is rejected"); return await Task.FromResult(LoginRequestResults.Rejected); case CredentialState.Requesting: case CredentialState.Alive: break; } Thread.Sleep(200); if (ct.IsCancellationRequested) break; // 等待指定的时间,然后向申请端发送超时 if (sw.Elapsed.TotalSeconds > CredentialManager.REQ_LOGIN_CRED_LIFT_TIME_SEC) break; } // 等待确认超时 return LoginRequestResults.Timeout; }); } /// /// 登录。 /// /// 用户名 /// 密码 /// 角色ID /// 发起登录请求的客户端信息 /// /// internal async Task Login(string userName, string password, string roleID, LoginClientInfo clientInfo) { var endPoint = GetCurrentWCFClientEndPoint(); try { var accountList = RoleLoader.AccountList; var matchedAccount = accountList.FirstOrDefault(x => x.LoginName == userName); if (matchedAccount == null) // 用户不存在 return await Task.FromResult(new LoginResult(LoginRequestResults.NoMatchUser)); if (matchedAccount.Password != password) // 密码错误 return await Task.FromResult(new LoginResult(LoginRequestResults.WrongPwd)); // 查找当前用户的登录角色是否存在 var matchedRole = matchedAccount.RoleIDs.FirstOrDefault(x => x == roleID); if (matchedRole == null) // 找不到角色 return await Task.FromResult(new LoginResult(LoginRequestResults.NoMatchRole)); // 创建新凭据 var requestingCred = new Credential(CredentialManager.GenerateToken(), matchedAccount) { LoginIP = endPoint.Address, LoginPort = endPoint.Port, ClientInfo = clientInfo, RoleID = roleID }; if (CredentialManager.Instance.LoggedInCount == 0 && CredentialManager.Instance.LoginRequestingCount == 0) { // 添加凭据到登录请求列表,Grant时需要请求列表中存在此凭据。 CredentialManager.Instance.AddRequestingList(requestingCred); // 没有用户登录,直接发放凭据 CredentialManager.Instance.Grant(requestingCred); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, $"{userName}@{requestingCred.LoginIP}:{requestingCred.LoginPort}"); return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, requestingCred)); } if (CredentialManager.Instance.IsSupportMultiUserLogin) { // 支持多用户同时登录 // 如果当前用户名正在请求登录,则拒绝发起新的请求。 if (CredentialManager.Instance.GetRequestingCredential(userName) != null) return await Task.FromResult(new LoginResult(LoginRequestResults.RequstingLogin)); // 添加凭据到登录请求列表,Grant时需要请求列表中存在此凭据。 CredentialManager.Instance.AddRequestingList(requestingCred); // 查找当前请求的用户是否已经登录 var loggedCred = CredentialManager.Instance.GetCredential(userName); if (loggedCred == null) { // 用户未登录,直接放行凭据 CredentialManager.Instance.Grant(requestingCred); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, $"{userName}@{requestingCred.LoginIP}:{requestingCred.LoginPort}"); return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, requestingCred)); } // 否则等待已登录的Session确认 var ret = await RequestAndWaitLoginConfirmation(loggedCred, requestingCred); return ret == LoginRequestResults.Confirmed ? new LoginResult(ret, requestingCred) : new LoginResult(ret); } else { // 仅支持单一用户登录 if (CredentialManager.Instance.LoggedInCount > 1) { EV.PostWarningLog(ModuleName.System.ToString(), $"{nameof(CredentialManager)} does not support multiple users login but more than one credentials found."); } // 如果有正在请求登录,则拒绝发起新的请求。 if (CredentialManager.Instance.LoginRequestingCount > 0) return await Task.FromResult(new LoginResult(LoginRequestResults.RequstingLogin)); // 添加凭据到登录请求列表,Grant时需要请求列表中存在此凭据。 CredentialManager.Instance.AddRequestingList(requestingCred); // 已登录的用户凭据 var loggedCred = CredentialManager.Instance.GetCredential(); var ret = await RequestAndWaitLoginConfirmation(loggedCred, requestingCred); return ret == LoginRequestResults.Confirmed ? new LoginResult(ret, requestingCred) : new LoginResult(ret); } } catch (Exception ex) { LOG.Error(ex.Message, ex); return await Task.FromResult(new LoginResult(LoginRequestResults.Error)); } finally { } } /// /// 登录请求发起端取消登录请求。 /// /// internal void CancelLoginRequest(string userName) { CredentialManager.Instance.Cancel(userName); } /// /// 确认登录请求。 /// /// internal void ConfirmedLoginRequest(string userName) { CredentialManager.Instance.Accept(userName); } /// /// 拒绝登录请求。 /// /// internal void RejectLoginRequest(string userName) { CredentialManager.Instance.Reject(userName); } internal void Logout(Guid myToken) { var loginCred = CredentialManager.Instance.GetCredential(myToken); if (loginCred != null) { EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedOff, $"{loginCred.AccountInfo.LoginName}@{loginCred.LoginIP}:{loginCred.LoginPort}"); } CredentialManager.Instance.Remove(myToken); } #endregion } }