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

266 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}