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

311 lines
9.4 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.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 SemaphoreSlim _syncRoot = new SemaphoreSlim(1, 1);
#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) });
}
}
/// <summary>
/// 获取WCF客户端IP信息。
/// </summary>
/// <returns></returns>
private 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 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 > 30)
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>
public async Task<LoginResult> 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
{
}
}
/// <summary>
/// 登录请求发起端取消登录请求。
/// </summary>
/// <param name="userName"></param>
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
}
}