密码规则匹配

规则如下:

  • 密码格式:6-16位数字字母组合
  • 不包含特殊字符。
  • 必须同时包含数字、大写字母,小写字母3种字符,区分大小写。
  • 连续3位及以上数字不能连续(例如123、876)
  • 连续3位及以上的字母不能连续(例如abc、cba)

代码实现:

<?php
define('PWD_MAX_LENGTH', 16);
define('PWD_MIN_LENGTH', 6);

/**
* @time 2016年8月29日11:52:29
*/
class User_Common {

/**
* 密码格式:6-16位数字字母组合
* 不包含特殊字符。
* 必须同时包含数字、大写字母,小写字母3种字符,区分大小写。
* 连续3位及以上数字不能连续(例如123、876)
* 连续3位及以上的字母不能连续(例如abc、cba)
* @param $password
* @return bool
* @throws \Exception
*/
public static function checkPassword($password) {
self::pwdLengthCheck($password);
self::pwdCharValid($password);
}

/**
* @param $password
* @return bool
* @throws \Exception
*/
private static function pwdCharValid($password) {
if (!ctype_alnum($password)) {
throw new Exception('不包含特殊字符', 10002);
}
$includeNumber = false;
$includeUpperLetter = false;
$includeLowerLetter = false;
$length = strlen($password);

for ($i=0; $i < $length; $i++) {
$char = $password[$i];
$includeUpperLetter = (!$includeUpperLetter && ctype_upper($char))
? true : $includeUpperLetter;
$includeNumber = (!$includeNumber && ctype_digit($char))
? true : $includeNumber;
$includeLowerLetter = (!$includeLowerLetter && ctype_lower($char))
? true : $includeLowerLetter;
if ($i != 0 && !empty($password[$i+1])
&& abs(ord($password[$i]) - ord($password[$i-1])) <=1
&& ord($password[$i]) - ord($password[$i-1]) == ord($password[$i+1]) - ord($password[$i])) {
throw new Exception('连续3位及以上数字或字母不能连续(例如123、876)', 10004);
}
}
if ($includeLowerLetter && $includeNumber && $includeUpperLetter) {
return 2;
} else {
throw new Exception('必须同时包含数字、大写字母,小写字母3种字符,区分大小写', 10003);
}
}

private static function pwdLengthCheck($password) {
if (strlen($password) > PWD_MAX_LENGTH || strlen($password) < PWD_MIN_LENGTH) {
throw new Exception('密码格式:6-16位数字字母组合', 10001);
}
}

/**
* 检查是否为1开头的11位数字手机号
* @param int $phoneNumber
* @return boolean 是否匹配
*/
public static function checkPhoneNumber($phoneNumber) {
return preg_match('/^1\d{10}$/', $phoneNumber);
}
}



try {
var_dump(User_Common::checkPassword('AbA001'));
} catch (Exception $e) {
echo $e->getMessage();
}

分析:

  • 总体来讲是利用了 ASCII 码 实现的密码规则验证。
  • 先初始化大小写字母和数字的 ASCII 码范围的数组
  • 然后遍历传过来的字符串,判断字符是否在数组中。
  • 连续性的判断是使用 ASCII 码的差值来判断,连续3个差值相等的并且差值小于等于1的字符,就判断为连续字符。

结语:

本来想通过正则表达式来实现,但连续字符的判断用正则实在是太难实现,而且效率还非常低。我在 segmentfault 上面问了一个问题密码规则正则匹配,另外问题:为什么正则表达式效率低?,最终我还是决定选择使用逻辑代码判断的方式来实现这个功能。 在实际编程时,如果密码不匹配,我希望能拿到不匹配的原因,所以我使用了异常机制。