移除EV.PostLoginBySameUser方法,改为UI端轮训LoginRequest状态凭据。
优化CredentialManager对于Requesting凭据字典的处理,在OnTimer方法中检测KeepAlive时移除RequestCanceled和Rejected状态的凭据。
This commit is contained in:
SL 2023-09-18 14:17:34 +08:00
parent 6957751858
commit ed26bf6bd1
10 changed files with 72 additions and 124 deletions

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Aitex.Core.RT.DBCore;
using Aitex.Core.RT.Event;
using Aitex.Core.Util;
namespace Aitex.Core.Account;
@ -113,10 +112,7 @@ public class CredentialManager : Singleton<CredentialManager>
{
var cred = kvp.Value;
if ((DateTime.Now - cred.LastAliveTime).TotalSeconds > LOGIN_CRED_KEEP_ALIVE_TIMEOUT_SEC)
{
loginRemovableList.Add(cred.Token);
EV.PostLoginBySameUser(cred.Token, new Credential());
}
}
if (loginRemovableList.Count > 0)
@ -138,13 +134,30 @@ public class CredentialManager : Singleton<CredentialManager>
var cred = kvp.Value;
if ((DateTime.Now - cred.LastAliveTime).TotalSeconds > REQ_LOGIN_CRED_LIFT_TIME_SEC)
requestRemovableList.Add(kvp.Key);
// 移除被取消或拒绝的凭据
if(cred.State is CredentialState.RequestCanceled or CredentialState.Reject)
requestRemovableList.Add(kvp.Key);
}
if (requestRemovableList.Count > 0)
{
foreach (var loginName in requestRemovableList)
{
WriteHistory(_dictCredentialsRequesting[loginName], "REQ EXPIRED");
var reason = "";
switch (_dictCredentialsRequesting[loginName].State)
{
case CredentialState.RequestCanceled:
reason = "REQ CANCELED";
break;
case CredentialState.Reject:
reason = "REQ REJECTED";
break;
default:
reason = "REQ EXPIRED";
break;
}
WriteHistory(_dictCredentialsRequesting[loginName], reason);
_dictCredentialsRequesting.Remove(loginName);
}
}
@ -197,7 +210,7 @@ public class CredentialManager : Singleton<CredentialManager>
/// <summary>
/// 报告客户端处于活动状态。
/// </summary>
/// <param name="cred">客户端登录凭据</param>
/// <param name="myToken">客户端登录凭据</param>
/// <returns></returns>
public CredentialKeepAliveResults KeepAlive(Guid myToken)
{
@ -209,9 +222,13 @@ public class CredentialManager : Singleton<CredentialManager>
// 如果当前用户名在请求登录列表中则返回CredentialKeepAliveResults.RequestingLogin通知
// 已登录的客户端,当前用户正在请求异地登录。
return _dictCredentialsRequesting.ContainsKey(loginCred.AccountInfo.LoginName)
? CredentialKeepAliveResults.RequestingLogin
: CredentialKeepAliveResults.Alive;
if (_dictCredentialsRequesting.TryGetValue(loginCred.AccountInfo.LoginName,
out var requestingLoginCred) && requestingLoginCred.State == CredentialState.Requesting)
{
return CredentialKeepAliveResults.RequestingLogin;
}
return CredentialKeepAliveResults.Alive;
}
return CredentialKeepAliveResults.NotFound;
@ -282,21 +299,6 @@ public class CredentialManager : Singleton<CredentialManager>
}
}
/// <summary>
/// 从登录请求列表中移除指定的凭据。
/// </summary>
/// <param name="cred"></param>
public void RemoveRequesting(Credential cred)
{
lock (_syncRoot)
{
if(_dictCredentialsRequesting.TryGetValue(cred.AccountInfo.LoginName, out var reqCredential))
WriteHistory(reqCredential, "REQ CANCEL");
_dictCredentialsRequesting.Remove(cred.AccountInfo.LoginName);
}
}
/// <summary>
/// 获取指定用户名的登录凭据。
/// </summary>

View File

@ -25,6 +25,11 @@ public enum CredentialState
/// </summary>
Requesting,
/// <summary>
/// 登录请求被取消。
/// </summary>
RequestCanceled,
/// <summary>
/// 凭据有效。
/// </summary>

View File

@ -81,14 +81,6 @@ namespace Aitex.Core.RT.Event
}
}
public static void PostLoginBySameUser(Guid token, Credential requestedCredential)
{
if (InnerEventManager != null)
{
InnerEventManager.PostLoginBySameUser(token, requestedCredential);
}
}
public static void PostSoundMessage(string message)
{
if (InnerEventManager != null)

View File

@ -231,17 +231,6 @@ namespace Aitex.Core.RT.Event
return true;
});
OP.Subscribe("System.Diagnosis.GenLoginBySameUserEvent", (s, args) =>
{
var token = (Guid)args[0];
var cred = CredentialManager.Instance.GetCredential(token);
if(cred != null)
PostLoginBySameUser(cred.Token, new Credential());
else
EV.PostWarningLog("Diagnosis", $"The credential with token {token} does not found.");
return true;
});
#endregion
}
@ -473,19 +462,6 @@ namespace Aitex.Core.RT.Event
_writerToLog.WriteEvent(eventItem);
}
public void PostLoginBySameUser(Guid token, Credential requestedCredential)
{
var eventItem = new EventItem
{
Type = EventType.LoginBySameUser_Notify,
Description = token.ToString(),
OccuringTime = DateTime.Now,
Tag = requestedCredential
};
_eventQueue.Enqueue(eventItem);
_writerToLog.WriteEvent(eventItem);
}
public void PostSoundMessage(string message)
{
var eventItem = new EventItem

View File

@ -50,12 +50,6 @@ namespace Aitex.Core.RT.Event
/// RT通知消息事件。
/// </summary>
[EnumMember]
HostNotification = 6,
/// <summary>
/// 用户异地登录通知。
/// </summary>
[EnumMember]
LoginBySameUser_Notify = 7
HostNotification = 6
}
}

View File

@ -23,8 +23,6 @@ namespace Aitex.Core.RT.Event
void PostKickoutMessage(string message);
void PostLoginBySameUser(Guid token, Credential requestedCredential);
void PostSoundMessage(string message);
List<EventItem> GetAlarmEvent();

View File

@ -12,6 +12,7 @@ using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using Aitex.Core.WCF;
using DocumentFormat.OpenXml.Wordprocessing;
using MECF.Framework.Common.Account.Extends;
using MECF.Framework.Common.Equipment;
@ -122,8 +123,6 @@ namespace MECF.Framework.Common.Account
return await Task.Run(async () =>
{
// 通知已登录的用户,其它客户端使用该用户名登录,是否允许登录
EV.PostLoginBySameUser(userLoggedIn.Token, userRequesting);
var ct = userRequesting.LoginRequestCancellationTokenSource.Token;
// 等待已登录的凭据被移除(客户端注销)
@ -132,27 +131,33 @@ namespace MECF.Framework.Common.Account
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);
}
switch (userRequesting.State)
{
case 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");
case CredentialState.Reject or CredentialState.RequestCanceled:
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);
}
return await Task.FromResult(LoginRequestResults.Rejected);
Thread.Sleep(1000);
ct.ThrowIfCancellationRequested();
case CredentialState.Requesting:
case CredentialState.Alive:
break;
}
Thread.Sleep(200);
if (ct.IsCancellationRequested)
break;
// 等待指定的时间,然后向申请端发送超时
if (sw.Elapsed.TotalSeconds > 40)
@ -279,8 +284,9 @@ namespace MECF.Framework.Common.Account
if (cred != null)
{
cred.LoginRequestCancellationTokenSource.Cancel();
}
}
cred.State = CredentialState.RequestCanceled;
}
}
/// <summary>
/// 确认登录请求。

View File

@ -10,8 +10,7 @@
Title="Login Request"
ResizeMode="CanResize"
WindowStyle="None"
WindowStartupLocation="CenterScreen"
Closing="LoginRequestWaitDialog_OnClosing">
WindowStartupLocation="CenterScreen">
<WindowChrome.WindowChrome>
<WindowChrome ResizeBorderThickness="0" CaptionHeight="0"/>
</WindowChrome.WindowChrome>

View File

@ -1,6 +1,5 @@
using Aitex.Core.Account;
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@ -27,10 +26,12 @@ namespace MECF.Framework.UI.Client.ClientBase.Dialog
_progCountDown = new Progress<double>(countDown =>
{
// 更新Cancel按钮倒计时数值
// 如果倒计时为0则关闭当前窗口
if (countDown > 0)
BtnCancel.Content = $"Cancel ({countDown:F0}s)";
else
DialogResult = false;
DialogResult = false; // canceled
});
}
@ -39,6 +40,7 @@ namespace MECF.Framework.UI.Client.ClientBase.Dialog
base.OnSourceInitialized(e);
var closeTime = DateTime.Now.Add(TimeSpan.FromSeconds(CredentialManager.REQ_LOGIN_DIALOG_LIFT_TIME__SEC));
var ct = _ctsCountDownTask?.Token;
Task.Run(() =>
{
while (true)
@ -46,15 +48,17 @@ namespace MECF.Framework.UI.Client.ClientBase.Dialog
var timeRemained = (closeTime - DateTime.Now).TotalSeconds;
if (timeRemained <= 0)
{
// force to reject
// force to cancel
_progCountDown.Report(0);
break;
}
_progCountDown.Report(timeRemained);
Thread.Sleep(500);
if (_ctsCountDownTask.Token.IsCancellationRequested)
// 操作被取消
if (ct is { IsCancellationRequested: true })
break;
}
});
@ -62,8 +66,7 @@ namespace MECF.Framework.UI.Client.ClientBase.Dialog
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
{
_ctsCountDownTask.Cancel();
DialogResult = false;
Cancel();
}
public void Accepted()
@ -72,7 +75,7 @@ namespace MECF.Framework.UI.Client.ClientBase.Dialog
DialogResult = true;
}
private void LoginRequestWaitDialog_OnClosing(object sender, CancelEventArgs e)
public void Cancel()
{
_ctsCountDownTask.Cancel();
DialogResult = false;

View File

@ -124,33 +124,6 @@ namespace UserLoginTester
case EventType.KickOut_Notify:
break;
case EventType.LoginBySameUser_Notify:
if (Guid.TryParse(evt.Description, out var token) && evt.Tag is Credential requestingCred)
{
if (_loginCred != null && token == _loginCred.Token)
{
Console.WriteLine(
@"Some users is requesting to login the system, you will be forced to switch to read-only mode, do you agree?");
__prompt:
Console.Write("[Y]es/[N]o: ");
var ack = Console.ReadLine();
switch (ack)
{
case "Y":
AccountClient.Instance.Service.ConfirmLoginRequest(requestingCred.AccountInfo.LoginName);
AccountClient.Instance.Service.LogoutEx(_loginCred.Token);
break;
case "N":
AccountClient.Instance.Service.RejectLoginRequest(requestingCred.AccountInfo.LoginName);
break;
default:
goto __prompt;
}
}
}
break;
case EventType.Sound_Notify:
break;
case EventType.UIMessage_Notify: