270 lines
8.7 KiB
C#
270 lines
8.7 KiB
C#
// /************************************************************************
|
||
// * @file RecipeLockPanel.xaml.cs
|
||
// * @author Su Liang
|
||
// * @date 2023/03/08
|
||
// *
|
||
// * @copyright © 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.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");
|
||
}
|
||
|
||
#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);
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region Methods
|
||
|
||
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(OpPathSetNewPassword, 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())
|
||
return;
|
||
|
||
if (LockTarget.Effect is BlurEffect eff)
|
||
{
|
||
var aniBlurFadeOut = new DoubleAnimation(BLUR_RADIUS, 0, new Duration(TimeSpan.FromMilliseconds(300)));
|
||
eff.BeginAnimation(BlurEffect.RadiusProperty, aniBlurFadeOut);
|
||
}
|
||
|
||
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
|
||
|
||
|
||
|
||
}
|
||
} |