266 lines
7.6 KiB
C#
266 lines
7.6 KiB
C#
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<AccountExManager>
|
||
{
|
||
#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
|
||
|
||
/// <summary>
|
||
/// 返回角色加载器。
|
||
/// </summary>
|
||
public RoleLoader RoleLoader { get; private set; }
|
||
|
||
#endregion
|
||
|
||
#region Methods
|
||
|
||
/// <summary>
|
||
/// 初始化当前对象。
|
||
/// </summary>
|
||
/// <param name="enableService"></param>
|
||
/// <exception cref="ApplicationException"></exception>
|
||
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<WcfServiceManager>.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<string, CancellationTokenSource> _dictLoginCts = new ();
|
||
|
||
public async Task<LoginRequestResults> 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<LoginRequestResults> RequestLogin(string loginName, string password, string role,
|
||
LoginClientInfo clientInfo)
|
||
{
|
||
Task<LoginRequestResults> 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
|
||
}
|
||
}
|