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 () => { // 通知已登录的用户,其它客户端使用该用户名登录,是否允许登录 EV.PostLoginBySameUser(userLoggedIn.Token, userRequesting); var ct = userRequesting.LoginRequestCancellationTokenSource.Token; // 等待已登录的凭据被移除(客户端注销) var sw = new Stopwatch(); sw.Start(); while (true) { if (userRequesting.State == CredentialState.Confirmed) { // 客户端已注销,可以放行新的凭据 CredentialManager.Instance.Grant(userRequesting); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, userLoggedIn.AccountInfo.LoginName); return await Task.FromResult(LoginRequestResults.Confirmed); } if (userRequesting.State == CredentialState.Reject) { // 已登录用户拒绝了登录请求 EV.PostInfoLog(ModuleName.System.ToString(), $"User {userRequesting.AccountInfo.LoginName}'s login request from {userRequesting.LoginIP}:{userRequesting.LoginPort} is rejected"); CredentialManager.Instance.RemoveRequesting(userRequesting); return await Task.FromResult(LoginRequestResults.Rejected); } Thread.Sleep(1000); ct.ThrowIfCancellationRequested(); if (sw.Elapsed.TotalSeconds > 30) break; } // 等待客户端注销超时 return LoginRequestResults.Timeout; }); } /// /// 登录。 /// /// 用户名 /// 密码 /// 角色ID /// 发起登录请求的客户端信息 /// /// public 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 = CredentialManager.Instance.GetRequestingCredential(userName); if (requestingCred != null) { // 该用户名正在请求登录,拒绝新的登录请求 return await Task.FromResult(new LoginResult(LoginRequestResults.RequstingLogin)); } // 创建新凭据 var newCred = new Credential(CredentialManager.GenerateToken(), matchedAccount) { LoginIP = endPoint.Address, LoginPort = endPoint.Port }; // 添加凭据到登录请求列表,Grant时需要请求列表中存在此凭据。 CredentialManager.Instance.AddRequestingList(newCred); if (CredentialManager.Instance.LoggedInCount == 0) { // 没有用户登录,直接发放凭据 CredentialManager.Instance.Grant(newCred); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, userName); return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, newCred)); } if (CredentialManager.Instance.IsSupportMultiUserLogin) { // 查找当前请求的用户是否已经登录 var loggedCred = CredentialManager.Instance.GetCredential(userName); // 支持多用户同时登录 if (loggedCred == null) { // 用户未登录,直接放行凭据 CredentialManager.Instance.Grant(newCred); EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedIn, userName); return await Task.FromResult(new LoginResult(LoginRequestResults.Confirmed, newCred)); } var ret = await RequestAndWaitLoginConfirmation(loggedCred, newCred); return ret == LoginRequestResults.Confirmed ? new LoginResult(ret, newCred) : 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."); } var loggedCred = CredentialManager.Instance.GetCredential(userName); var ret = await RequestAndWaitLoginConfirmation(loggedCred, newCred); return ret == LoginRequestResults.Confirmed ? new LoginResult(ret, newCred) : new LoginResult(ret); } } catch (Exception ex) { LOG.Error(ex.Message, ex); return await Task.FromResult(new LoginResult(LoginRequestResults.Error)); } finally { } } /// /// 登录请求发起端取消登录请求。 /// /// public void CancelLoginRequest(string userName) { var cred = CredentialManager.Instance.GetRequestingCredential(userName); if (cred != null) { cred.LoginRequestCancellationTokenSource.Cancel(); } } public void ConfirmedLoginRequest(Credential requestingCred) { var cred = CredentialManager.Instance.GetRequestingCredential(requestingCred.Token); if (cred != null) cred.State = CredentialState.Confirmed; } public void RejectLoginRequest(Credential requestingCred) { var cred = CredentialManager.Instance.GetRequestingCredential(requestingCred.Token); if (cred != null) cred.State = CredentialState.Reject; } internal void Logout(Guid token) { var cred = CredentialManager.Instance.GetCredential(token); if (cred != null) { EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedOff, cred.AccountInfo.LoginName); } CredentialManager.Instance.Remove(token); } #endregion } }