351 lines
13 KiB
C#
351 lines
13 KiB
C#
using System;
|
||
using System.Linq;
|
||
using System.Text.RegularExpressions;
|
||
|
||
namespace DeviceRepair.Models
|
||
{
|
||
/// <summary>
|
||
/// 密码策略对象
|
||
/// (目前没有存入后台数据库,目前基于客户端判断)
|
||
/// </summary>
|
||
public class PasswordStrategy
|
||
{
|
||
private static PasswordStrategy manager;
|
||
public static PasswordStrategy Instance
|
||
{
|
||
get
|
||
{
|
||
if (manager == null)
|
||
manager = new PasswordStrategy();
|
||
return manager;
|
||
}
|
||
}
|
||
|
||
#region Fields & Property
|
||
|
||
/// <summary>
|
||
/// 默认的特殊字符集
|
||
/// </summary>
|
||
private char[] DefaultSpecialCharacters = new char[]
|
||
{
|
||
'~','`','!','@','#','$','%','^','&','*','(',')','-','_','+','-','[',']','{','}','|','\\',':',';','"','\'','<','>','.','/','?'
|
||
};
|
||
|
||
/// <summary>
|
||
/// 最小密码长度
|
||
/// </summary>
|
||
public int PasswordMinLength
|
||
{
|
||
get;
|
||
set;
|
||
}
|
||
/// <summary>
|
||
/// 密码策略类型
|
||
/// </summary>
|
||
public PasswordStrategyType StrategyType
|
||
{
|
||
get;
|
||
set;
|
||
}
|
||
/// <summary>
|
||
/// 最小密码策略组合类型种类
|
||
/// </summary>
|
||
public int MinPasswordStrategyTypeCounts
|
||
{
|
||
get;
|
||
set;
|
||
}
|
||
/// <summary>
|
||
/// 密码有效期限
|
||
/// (以天为单位)
|
||
/// </summary>
|
||
public int PasswordValidDays
|
||
{
|
||
get; set;
|
||
}
|
||
/// <summary>
|
||
/// 密码错误连续重试次数
|
||
/// (超过连续重试次数后,账号将被锁定)
|
||
/// </summary>
|
||
public int PasswordContinueRetryCounts
|
||
{
|
||
get; set;
|
||
}
|
||
/// <summary>
|
||
/// 密码修改不允许重复的次数
|
||
/// (默认:连续5次密码修改不允许相同)
|
||
/// </summary>
|
||
public int PasswordAlterUnRepeatCounts
|
||
{
|
||
get; set;
|
||
}
|
||
/// <summary>
|
||
/// 系统允许的屏幕最大闲置时间(以分钟为单位)
|
||
/// (超过规定时间,系统屏幕将被锁定,需要账号登陆解锁屏幕)
|
||
/// </summary>
|
||
public int ScreenLockTimes
|
||
{
|
||
get; set;
|
||
}
|
||
#endregion
|
||
|
||
#region Ctor
|
||
|
||
public PasswordStrategy(PasswordStrategyType strategyType = PasswordStrategyType.UpperCaseLetter | PasswordStrategyType.LowerCaseLetter | PasswordStrategyType.Number | PasswordStrategyType.SpecialCharacter,
|
||
int passwordMinLength = 8, int minPasswordStrategyTypeCounts = 3, int passwordValidDays = 90, int passwordContinueRetryCounts = 5,
|
||
int passwordAlterUnRepeatCounts = 5, int screenLockTimes = 15)
|
||
{
|
||
StrategyType = strategyType;
|
||
PasswordMinLength = passwordMinLength;
|
||
MinPasswordStrategyTypeCounts = minPasswordStrategyTypeCounts;
|
||
PasswordValidDays = passwordValidDays;
|
||
PasswordContinueRetryCounts = passwordContinueRetryCounts;
|
||
PasswordAlterUnRepeatCounts = passwordAlterUnRepeatCounts;
|
||
ScreenLockTimes = screenLockTimes;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Methods
|
||
|
||
/// <summary>
|
||
/// 密码校验
|
||
/// </summary>
|
||
/// <param name="password"></param>
|
||
/// <param name="msg"></param>
|
||
/// <returns></returns>
|
||
public bool Validate(string password, out string msg)
|
||
{
|
||
try
|
||
{
|
||
msg = "密码校验成功!";
|
||
|
||
if (string.IsNullOrEmpty(password))
|
||
{
|
||
msg = "密码为空!";
|
||
return false;
|
||
}
|
||
if (password.Length < PasswordMinLength)
|
||
{
|
||
msg = $"密码至少位{PasswordMinLength}位!";
|
||
return false;
|
||
}
|
||
|
||
#region 校验密码强度
|
||
int iCurrentPasswordIntensity = 0;
|
||
|
||
if ((StrategyType & PasswordStrategyType.UpperCaseLetter) != 0)
|
||
{
|
||
//策略中要求大写字母验证
|
||
if (CheckCharacter(password, PasswordStrategyType.UpperCaseLetter))
|
||
{
|
||
iCurrentPasswordIntensity++;
|
||
}
|
||
}
|
||
if ((StrategyType & PasswordStrategyType.LowerCaseLetter) != 0)
|
||
{
|
||
//策略中要求小写字母验证
|
||
if (CheckCharacter(password, PasswordStrategyType.LowerCaseLetter))
|
||
{
|
||
iCurrentPasswordIntensity++;
|
||
}
|
||
}
|
||
if ((StrategyType & PasswordStrategyType.Number) != 0)
|
||
{
|
||
//策略中要求数字验证
|
||
if (CheckCharacter(password, PasswordStrategyType.Number))
|
||
{
|
||
iCurrentPasswordIntensity++;
|
||
}
|
||
}
|
||
if ((StrategyType & PasswordStrategyType.SpecialCharacter) != 0)
|
||
{
|
||
//策略中要求特殊字符验证
|
||
if (CheckCharacter(password, PasswordStrategyType.SpecialCharacter))
|
||
{
|
||
iCurrentPasswordIntensity++;
|
||
}
|
||
}
|
||
if (iCurrentPasswordIntensity < MinPasswordStrategyTypeCounts)
|
||
{
|
||
msg = $"密码不合符密码强度策略(大写字母/小写字母/数字/特殊字符中最好满足其中{MinPasswordStrategyTypeCounts}种)!";
|
||
return false;
|
||
}
|
||
#endregion
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"密码校验时发生异常:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 校验密码是否符合单一密码策略
|
||
/// </summary>
|
||
/// <param name="password"></param>
|
||
/// <param name="strategyType"></param>
|
||
/// <returns></returns>
|
||
private bool CheckCharacter(string password, PasswordStrategyType strategyType)
|
||
{
|
||
string regexExpresion = string.Empty;
|
||
switch (strategyType)
|
||
{
|
||
case PasswordStrategyType.UpperCaseLetter:
|
||
regexExpresion = "[A-Z]";
|
||
break;
|
||
case PasswordStrategyType.LowerCaseLetter:
|
||
regexExpresion = "[a-z]";
|
||
break;
|
||
case PasswordStrategyType.Number:
|
||
regexExpresion = "[0-9]";
|
||
break;
|
||
case PasswordStrategyType.SpecialCharacter:
|
||
if ((password.Where(A => { return DefaultSpecialCharacters.Contains(A); })?.Count() ?? 0) > 0)
|
||
{
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
return Regex.IsMatch(password, regexExpresion);
|
||
}
|
||
|
||
#region 随机产生符合当前密码策略的密码
|
||
|
||
/// <summary>
|
||
/// 随机产生符合当前密码策略的密码
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public string GenRandomPassword()
|
||
{
|
||
string vPwdTmp = string.Empty;
|
||
if ((StrategyType & PasswordStrategyType.UpperCaseLetter) != 0)
|
||
{
|
||
vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.UpperCaseLetter, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
|
||
}
|
||
if ((StrategyType & PasswordStrategyType.LowerCaseLetter) != 0)
|
||
{
|
||
vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.LowerCaseLetter, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
|
||
}
|
||
if ((StrategyType & PasswordStrategyType.Number) != 0)
|
||
{
|
||
vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.Number, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
|
||
}
|
||
if ((StrategyType & PasswordStrategyType.SpecialCharacter) != 0)
|
||
{
|
||
vPwdTmp += GetRandomCharByStrategy(PasswordStrategyType.SpecialCharacter, (new Random(~unchecked((int)DateTime.Now.Ticks))).Next(3, 5));
|
||
}
|
||
if (vPwdTmp.Length < PasswordMinLength)
|
||
{
|
||
vPwdTmp += RandomString.GetRandomString(PasswordMinLength - vPwdTmp.Length,
|
||
((StrategyType & PasswordStrategyType.Number) != 0), ((StrategyType & PasswordStrategyType.LowerCaseLetter) != 0),
|
||
((StrategyType & PasswordStrategyType.UpperCaseLetter) != 0),
|
||
((StrategyType & PasswordStrategyType.SpecialCharacter) != 0));
|
||
}
|
||
return RandomString.RandomChangeStringSequence(vPwdTmp);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按照密码策略随机产生指定位数的字符
|
||
/// </summary>
|
||
/// <param name="pwdStrategyType">密码策略</param>
|
||
/// <param name="iRandomLength">随机位数</param>
|
||
/// <returns></returns>
|
||
private string GetRandomCharByStrategy(PasswordStrategyType pwdStrategyType, int iRandomLength)
|
||
{
|
||
string vRandStrPwd = string.Empty;
|
||
switch (pwdStrategyType)
|
||
{
|
||
case PasswordStrategyType.UpperCaseLetter:
|
||
vRandStrPwd = GenerateRandomLetter(iRandomLength);
|
||
break;
|
||
case PasswordStrategyType.LowerCaseLetter:
|
||
vRandStrPwd = GenerateRandomLetter(iRandomLength, true);
|
||
break;
|
||
case PasswordStrategyType.Number:
|
||
vRandStrPwd = GenerateCheckCodeNum(iRandomLength);
|
||
break;
|
||
case PasswordStrategyType.SpecialCharacter:
|
||
vRandStrPwd = GenerateRandomSpecialCharacter(iRandomLength);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
return vRandStrPwd;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 随机生成字母
|
||
/// </summary>
|
||
/// <param name="iRandomLength">生成长度</param>
|
||
/// <param name="bLower">是否小写字母</param>
|
||
/// <returns></returns>
|
||
private string GenerateRandomLetter(int iRandomLength, bool bLower = false)
|
||
{
|
||
char[] Pattern = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
|
||
if (bLower)
|
||
{
|
||
Pattern = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
|
||
}
|
||
|
||
string result = "";
|
||
int n = Pattern.Length;
|
||
Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
|
||
for (int i = 0; i < iRandomLength; i++)
|
||
{
|
||
int rnd = random.Next(0, n);
|
||
result += Pattern[rnd];
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 随机生成数字
|
||
/// </summary>
|
||
/// <param name="iRandomLength"></param>
|
||
/// <returns></returns>
|
||
private string GenerateCheckCodeNum(int iRandomLength)
|
||
{
|
||
int rep = 0;
|
||
string str = string.Empty;
|
||
long num2 = DateTime.Now.Ticks + rep;
|
||
rep++;
|
||
Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
|
||
for (int i = 0; i < iRandomLength; i++)
|
||
{
|
||
int num = random.Next();
|
||
str = str + ((char)(0x30 + ((ushort)(num % 10)))).ToString();
|
||
}
|
||
return str;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 随机生成特殊字符
|
||
/// </summary>
|
||
/// <param name="iRandomLength">生成长度</param>
|
||
/// <returns></returns>
|
||
private string GenerateRandomSpecialCharacter(int iRandomLength)
|
||
{
|
||
string result = "";
|
||
int n = DefaultSpecialCharacters.Length;
|
||
Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
|
||
for (int i = 0; i < iRandomLength; i++)
|
||
{
|
||
int rnd = random.Next(0, n);
|
||
result += DefaultSpecialCharacters[rnd];
|
||
}
|
||
return result;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
}
|
||
}
|