Sic.Framework-Nanjing-Baishi/MECF.Framework.Common/MECF/Framework/Common/Account/AccountExManager.cs

322 lines
10 KiB
C#
Raw Normal View History

2023-04-13 11:51:03 +08:00
using System;
using System.Diagnostics;
2023-04-13 11:51:03 +08:00
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
2023-04-13 11:51:03 +08:00
using System.Threading;
using System.Threading.Tasks;
2023-04-13 11:51:03 +08:00
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;
2023-04-13 11:51:03 +08:00
namespace MECF.Framework.Common.Account
{
/// <summary>
/// 账号管理器。
/// </summary>
2023-04-13 11:51:03 +08:00
public class AccountExManager : Singleton<AccountExManager>
{
#region Variables
2023-04-13 11:51:03 +08:00
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()
{
}
2023-04-13 11:51:03 +08:00
#endregion
#region Properties
/// <summary>
2023-09-05 09:27:56 +08:00
/// 返回角色加载器。
/// </summary>
2023-04-13 11:51:03 +08:00
public RoleLoader RoleLoader { get; private set; }
#endregion
#region Methods
/// <summary>
/// 初始化当前对象。
/// </summary>
/// <param name="enableService"></param>
/// <exception cref="ApplicationException"></exception>
2023-04-13 11:51:03 +08:00
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))
2023-04-13 11:51:03 +08:00
{
File.Copy(_scAccountTemplateFile, _scAccountLocalFile);
Thread.Sleep(10);
2023-04-13 11:51:03 +08:00
}
// 模板文件不存在
throw new ApplicationException("Can not initialize account configuration file, " +
_scAccountTemplateFile);
2023-04-13 11:51:03 +08:00
}
// 默认不支持多用户登录。
CredentialManager.Instance.Initialize(false);
2023-04-13 11:51:03 +08:00
RoleLoader = new RoleLoader(_scAccountLocalFile);
RoleLoader.Load();
2023-09-05 10:20:23 +08:00
2023-04-13 11:51:03 +08:00
if (enableService)
{
Singleton<WcfServiceManager>.Instance.Initialize(new Type[1] { typeof(AccountService) });
}
}
/// <summary>
/// 获取WCF客户端IP信息。
/// </summary>
/// <returns></returns>
private static RemoteEndpointMessageProperty GetCurrentWCFClientEndPoint()
{
var context = OperationContext.Current;
var prop = context.IncomingMessageProperties;
var endpoint =
prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return endpoint;
}
/// <summary>
/// 等待已登录的用户确认是否允许新的登录请求。
/// </summary>
/// <param name="userLoggedIn">已登录用户的凭据。</param>
/// <param name="userRequesting">新用户的登录凭据。</param>
/// <returns></returns>
private static async Task<LoginRequestResults> 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 > 40)
break;
}
// 等待确认超时
return LoginRequestResults.Timeout;
});
}
/// <summary>
/// 登录。
/// </summary>
/// <param name="userName">用户名</param>
/// <param name="password">密码</param>
/// <param name="roleID">角色ID</param>
/// <param name="clientInfo">发起登录请求的客户端信息</param>
/// <returns><see cref="LoginRequestResults"/></returns>
/// <exception cref="TimeoutException"></exception>
internal async Task<LoginResult> Login(string userName, string password, string roleID,
LoginClientInfo clientInfo)
2023-04-13 11:51:03 +08:00
{
var endPoint = GetCurrentWCFClientEndPoint();
try
2023-04-13 11:51:03 +08:00
{
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));
// 同一用户名不可重复请求登录。
if (CredentialManager.Instance.GetRequestingCredential(userName) != null)
{
// 该用户名正在请求登录,拒绝新的登录请求
return await Task.FromResult(new LoginResult(LoginRequestResults.RequstingLogin));
}
// 创建新凭据
var requestingCred = new Credential(CredentialManager.GenerateToken(), matchedAccount)
2023-04-13 11:51:03 +08:00
{
LoginIP = endPoint.Address,
LoginPort = endPoint.Port,
ClientInfo = clientInfo,
RoleID = roleID
};
// 添加凭据到登录请求列表Grant时需要请求列表中存在此凭据。
CredentialManager.Instance.AddRequestingList(requestingCred);
if (CredentialManager.Instance.LoggedInCount == 0)
2023-04-13 11:51:03 +08:00
{
// 没有用户登录,直接发放凭据
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)
{
// 查找当前请求的用户是否已经登录
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));
}
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.");
}
var loggedCred = CredentialManager.Instance.GetCredential(userName);
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
{
}
2023-04-13 11:51:03 +08:00
}
/// <summary>
/// 登录请求发起端取消登录请求。
/// </summary>
/// <param name="userName"></param>
internal void CancelLoginRequest(string userName)
{
var cred = CredentialManager.Instance.GetRequestingCredential(userName);
if (cred != null)
{
cred.LoginRequestCancellationTokenSource.Cancel();
}
}
/// <summary>
/// 确认登录请求。
/// </summary>
/// <param name="userName"></param>
internal void ConfirmedLoginRequest(string userName)
{
var cred = CredentialManager.Instance.GetRequestingCredential(userName);
if (cred != null)
cred.State = CredentialState.Confirmed;
}
/// <summary>
/// 拒绝登录请求。
/// </summary>
/// <param name="userName"></param>
internal void RejectLoginRequest(string userName)
{
var cred = CredentialManager.Instance.GetRequestingCredential(userName);
if (cred != null)
cred.State = CredentialState.Reject;
}
internal void Logout(Guid myToken)
2023-04-13 11:51:03 +08:00
{
var loginCred = CredentialManager.Instance.GetCredential(myToken);
if (loginCred != null)
{
EV.PostMessage(ModuleName.System.ToString(), EventEnum.UserLoggedOff,
$"{loginCred.AccountInfo.LoginName}@{loginCred.LoginIP}:{loginCred.LoginPort}");
2023-04-13 11:51:03 +08:00
}
CredentialManager.Instance.Remove(myToken);
2023-04-13 11:51:03 +08:00
}
#endregion
2023-04-13 11:51:03 +08:00
}
}