Sic.Framework/MECF.Framework.UI.Client/Ctrlib/Controls/PanelLocker.xaml.cs

296 lines
9.5 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.

// /************************************************************************
// * @file RecipeLockPanel.xaml.cs
// * @author Su Liang
// * @date 2023/03/08
// *
// * @copyright &copy Sicentury Inc.
// *
// * @brief
// *
// * @details
// *
// *
// * *****************************************************************************/
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using Aitex.Core.Util;
using MECF.Framework.Common.DataCenter;
using MECF.Framework.Common.OperationCenter;
using Panel = System.Windows.Controls.Panel;
namespace MECF.Framework.UI.Client.Ctrlib.Controls
{
public partial class PanelLocker
{
/// <summary>
/// 锁定时背景模糊半径
/// </summary>
private const double BLUR_RADIUS = 30;
/// <summary>
/// 默认密码admin的SHA256
/// </summary>
private const string DEF_PASSWORD = "8C6976E5B5410415BDE908BD4DEE15DFB167A9C873FC4BB8A81F6F2AB448A918";
// 阻止密码攻击定时器
private readonly DeviceTimer _timPassAttackDetector;
// 密码连续重试次数
private int _retryTimes;
// 自动锁定计时器
private readonly Stopwatch _swAutoLock;
private PeriodicJob _pjobAutoLock;
private int _timeoutAutoLock = int.MaxValue;
public PanelLocker()
{
InitializeComponent();
_timPassAttackDetector = new DeviceTimer();
_retryTimes = 0;
_pjobAutoLock = new PeriodicJob(int.MaxValue, () =>
{
Dispatcher.Invoke(Lock);
return true;
}, "AutoLockRecipeEditor");
// Lock面板加载后或重新激活后将焦点自动设置到PasswordBox
Loaded += (sender, args) =>
{
txtPass.Focus();
};
}
#region Dps
public static readonly DependencyProperty LockTargetProperty = DependencyProperty.Register(
nameof(LockTarget), typeof(Panel), typeof(PanelLocker), new PropertyMetadata(default(Panel), (sender, args) =>
{
// 初次拿到LockTarget对象时锁定对象
if (sender is PanelLocker pl && args.NewValue is Panel)
{
pl.Lock();
}
}));
/// <summary>
/// 设置或返回待锁定的界面
/// </summary>
public Panel LockTarget
{
get => (Panel)GetValue(LockTargetProperty);
set => SetValue(LockTargetProperty, value);
}
public static readonly DependencyProperty ScPathPasswordProperty = DependencyProperty.Register(
nameof(ScPathPassword), typeof(string), typeof(PanelLocker), new PropertyMetadata("System.Recipe.EditorPassword"));
/// <summary>
/// 设置或返回SC中存放密码的节点。
/// </summary>
public string ScPathPassword
{
get => (string)GetValue(ScPathPasswordProperty);
set => SetValue(ScPathPasswordProperty, value);
}
public static readonly DependencyProperty ScPathIsResetPasswordProperty = DependencyProperty.Register(
nameof(ScPathIsResetPassword), typeof(string), typeof(PanelLocker), new PropertyMetadata("System.Recipe.ResetEditorPassword"));
/// <summary>
/// 设置或返回SC中复位密码的节点。
/// </summary>
public string ScPathIsResetPassword
{
get => (string)GetValue(ScPathIsResetPasswordProperty);
set => SetValue(ScPathIsResetPasswordProperty, value);
}
public static readonly DependencyProperty OpPathSetNewPasswordProperty = DependencyProperty.Register(
nameof(OpPathSetNewPassword), typeof(string), typeof(PanelLocker), new PropertyMetadata("System.SetNewRecipeEditorPassword"));
/// <summary>
/// 设置或返回RT中注册的用于设置新密码的OP。
/// </summary>
public string OpPathSetNewPassword
{
get => (string)GetValue(OpPathSetNewPasswordProperty);
set => SetValue(OpPathSetNewPasswordProperty, value);
}
public static readonly DependencyProperty OpPathResetPasswordToDefaultProperty = DependencyProperty.Register(
nameof(OpPathResetPasswordToDefault), typeof(string), typeof(PanelLocker), new PropertyMetadata("System.ResetRecipeEditorPassword"));
public string OpPathResetPasswordToDefault
{
get => (string)GetValue(OpPathResetPasswordToDefaultProperty);
set => SetValue(OpPathResetPasswordToDefaultProperty, value);
}
#endregion
#region Methods
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
txtPass.Focus();
}
public void Lock()
{
txtPass.Password = "";
txtInfo.Text = "";
LockTarget.Effect = new BlurEffect();
Visibility = Visibility.Visible;
var aniBlurFadeOut = new DoubleAnimation(0, BLUR_RADIUS, new Duration(TimeSpan.FromMilliseconds(300)));
LockTarget.Effect.BeginAnimation(BlurEffect.RadiusProperty, aniBlurFadeOut);
// 停止自动锁定计时器
//_swAutoLock.Stop();
}
/// <summary>
/// 重置自动锁定超时计时器
/// </summary>
public void ResetAutoLockTimer()
{
_pjobAutoLock.Stop();
_pjobAutoLock.Start();
}
/// <summary>
/// 校验密码
/// </summary>
/// <returns></returns>
private bool CheckPassword()
{
// 是否请求重置密码
var reqResetPass = (bool)QueryDataClient.Instance.Service.GetConfig(ScPathIsResetPassword);
if (reqResetPass)
{
// 密码重置为admin
InvokeClient.Instance.Service.DoOperation(OpPathResetPasswordToDefault, DEF_PASSWORD);
}
// 读取系统配置中的Password如果读取失败使用默认密码admin
var passInSc = QueryDataClient.Instance.Service.GetConfig(ScPathPassword).ToString();
if (string.IsNullOrEmpty(passInSc))
passInSc = DEF_PASSWORD;
using (var sha = SHA256.Create())
{
var bytesHash = sha.ComputeHash(Encoding.UTF8.GetBytes(txtPass.Password));
var passInput = BitConverter.ToString(bytesHash).Replace("-", "");
if (passInput == passInSc)
{
txtPass.Password = "";
return true;
}
}
// 检测是否5s内重试密码5次以上。
if (_timPassAttackDetector.IsIdle() || _timPassAttackDetector.IsTimeout())
{
_timPassAttackDetector.Start(5000);
_retryTimes = 0;
}
else
{
_retryTimes++;
if (_retryTimes > 5)
{
btnUnlock.IsEnabled = false;
txtPass.IsEnabled = false;
// 如果5s内重试的次数大于5次禁用解锁窗口5秒钟
Task.Run(() =>
{
var deniedSec = 5;
while (deniedSec-- > 0)
{
Dispatcher.Invoke(() =>
{
txtInfo.Text = $"You have retried too many times, try again in {deniedSec} seconds";
});
Thread.Sleep(1000);
}
Dispatcher.Invoke(() =>
{
txtInfo.Text = "";
btnUnlock.IsEnabled = true;
txtPass.IsEnabled = true;
});
});
}
}
txtInfo.Text = "Incorrect Password";
txtPass.Password = "";
return false;
}
#endregion
#region Events
private void BtnUnlock_OnClick(object sender, RoutedEventArgs e)
{
if (!CheckPassword())
{
txtPass.Focus();
return;
}
if (LockTarget.Effect is BlurEffect eff)
{
var aniBlurFadeOut = new DoubleAnimation(BLUR_RADIUS, 0, new Duration(TimeSpan.FromMilliseconds(300)));
eff.BeginAnimation(BlurEffect.RadiusProperty, aniBlurFadeOut);
}
LockTarget.Effect = null;
this.Visibility = Visibility.Collapsed;
// 启动自动锁定计时器
/*var autoLockTimeout = (int)QueryDataClient.Instance.Service.GetConfig("System.RecipeEditorAutoLockTimeout");
if (autoLockTimeout <= 0)
autoLockTimeout = 60;
_swAutoLock.Interval = autoLockTimeout;
_swAutoLock.Start();*/
}
private void TxtPass_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
BtnUnlock_OnClick(this, new RoutedEventArgs());
}
#endregion
}
}