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
|
|||
|
}
|
|||
|
}
|