PHP的最严密码规则校验类

PHP的最严密码规则校验类

需求:

  1. 口令长度必须大于8位,长度限制为8-20个字符;
  2. 口令应包含大写字母、小写字母、数字、特殊符号,缺一不可(创建用户时的初始密码、用户修改密码时必须符合此规范);
  3. 口令中不得包含2位及以上的相同数字或字母。(如chrdw#11的最后2位,aa$HDHXT的前2位,sz&555pzc的中间3位);
  4. 口令中不得包含与账号相同的字母组合,含大小写组合。(如账号为guozw,密码为guoZW#16);            
  5. 不得使用与操作系统、数据库等相关的词组作为口令。(如root、admin、mysql、oralce、system);              
  6. 不得使用看似符合要求,实为3位及以上连续键盘序列组合作为口令。(如:123qweASD,1qaz@WSX等);   说明:建议考虑主键盘区、数字键盘区域,数字、字母、特殊字符正反向连续键盘序列组合。
  7. 口令不符合规则的拒绝登录,防止后台设置简单密码;  说明:未针对该规则做修改,当前实现仅在源头限制住,即:添加/修改用户密码时限制密码组成规则,但登录时仅校验密码正确性,不会再次校验密码组成规则

面对以上需求 ,特别是第六条,很奇葩有木有
废话不说上代码
 

<?php
/**
 *
 * An helper class focusing on password validation
 */
class HelperPassword
{
    const MAX_PASSWORD_LIMIT = 20;
    const MIN_PASSWORD_LIMIT = 8;
    //是否启用严格模式 留一个后门
    // 对应数据库字段ep_params key=【allow_password_sample】 = 0 表示严格
    //1表示启用严格模式 0表示不启用严格模式
    //这里严格模式与普通模式的区别仅仅是三连的规则
    //严格模式需要支持三连
    const STRICT_MODEL_STATUS = 1;
    const SAMPLE_MODEL_STATUS = 0;

    private static $forbidden_keywords = array("root","admin","oracle","system","mysql");
    private $algorithm;
    private $saltLength;
    private static $allow_special_characters = '~!@#$%^&*()[]{}|:\'+="<>?,./;\\\_-’‘?、。,;:“”」「【】·`《》¥…';
    private static $str_continuities = array(
        "1234567890 0987654321", //数字倒序
        "qwertyuiop asdfghjkl zxcvbnm QWERTYUIOP ASDFGHJKL ZXCVBNM", //主键盘顺序
        "poiuytrewq lkjhgfdsa mnbvcxz POIUYTREWQ LKJHGFDSA MNBVCXZ", //主键盘逆序
        "qaz wsx edc rfv tgb yhn ujm QAZ WSX EDC RFV TGB YHN UJM",//主键盘正向斜
        "zaq xsw cde vfr bgt nhy mju ZAQ XSW CDE VFR BGT NHY MJU",//主键盘正向斜逆序
        "esz rdx tfc ygv uhb ijn okm OKM IJN UHB YGV TFC RDX ESZ",//主键盘反向斜
        "zse xdr cft vgy bhu nji mko MKO NJI BHU VGY CFT XDR ZSE",//主键盘反向斜逆序
        "147 369 258 852 963 741" //小键盘
        //特殊字符不计算在内 否则无休止
    );

    public function __construct($algorithm, $saltLength) {
        $this->algorithm = $algorithm;
        $this->saltLength = $saltLength;
    }

    /**
     * 加密
     * @param $password
     *
     * @return string
     * @throws PasswordException
     */
    public function encrypt($password)
    {
        if (self::validate($password)) {
            $salt = HelperRandom::generateString($this->getSaltLength());
            return $salt . hash($this->getAlgorithm(), $salt . $password);
        }
        return "";
    }

    public function verify($password, $hash)
    {
        $salt = substr($hash, 0, $this->getSaltLength());
        $hashed = substr($hash, $this->getSaltLength());
        $result =  hash($this->getAlgorithm(), $salt.$password) === $hashed;
        return $result;
    }

    protected function getAlgorithm()
    {
        return $this->algorithm;
    }

    protected function getSaltLength()
    {
        return $this->saltLength;
    }

    /**
     * @param $password
     * @throws PasswordException
     */
    public static function validate($password, $username = "")
    {
        if (mb_strlen($password) > self::MAX_PASSWORD_LIMIT || mb_strlen($password) < self::MIN_PASSWORD_LIMIT) {
            throw new PasswordException("密码长度必须是8到20位");
        }
        $char_i = $char_a = $char_A = $char_t = array();
        $last_char = "";
        $list_char_3 = "";//连续三个字符
        $chars =preg_split('/(?<!^)(?!$)/u', $password ); //也行是中文标点
        foreach ($chars as $char) {
            if ($last_char === $char && !in_array($last_char,$char_t)) {
                throw new PasswordException("密码不能含有两个连续相同的数字或字母[$char]");
            }
            $list_char_3 .= $char;
            $last_char = $char;
            //判断三连
            $allow_password_sample = ParamModel::getVal("allow_password_sample");
            $password_model_status = ($allow_password_sample == 1)?
                self::SAMPLE_MODEL_STATUS:self::STRICT_MODEL_STATUS;
            if($password_model_status == self::STRICT_MODEL_STATUS) {
                if (strlen($list_char_3) >= 3) {
                    $list_char_3 = substr($list_char_3, strlen($list_char_3) - 3, 3);
                    foreach (self::$str_continuities as $str_continuity) {
                        if (strpos($str_continuity, $list_char_3) !== false) {
                            throw new PasswordException("密码不能包括连续的3个字符键盘键位[$list_char_3]");
                        }
                    }
                }
            }
            if (is_numeric($char)) {
                $char_i[] = $char;
                continue;
            }
            $str = ord($char);
            if ($str > 64 && $str < 91) {
                //大写字母
                $char_A[] = $char;
                continue;
            }
            if ($str > 96 && $str < 123) {
                //小写字母
                $char_a[] = $char;
                continue;
            }
            //这里的特殊字符指
            if(strpos(self::$allow_special_characters,$char) !== false){
                $char_t[] = $char;
                continue;
            }
            //其他一切字符
            throw new PasswordException("密码含有系统不允许的特殊字符");
        }
        if (empty($char_i) || empty($char_a) || empty($char_A) || empty($char_t)) {
            throw new PasswordException("密码必须同时含有大写、小写、数字和特殊字符");
        }
        //关键字
        $forbidden_key = self::$forbidden_keywords;
        if(!empty($username)) {
            $forbidden_key = array_merge(self::$forbidden_keywords, (array)strtolower($username));
        }
        foreach ($forbidden_key as $keyword) {
            if (strpos(strtolower($password), $keyword) !== false) {
                throw new PasswordException("密码包含系统禁止的词汇或用户名");
            }
        }
        return true;
    }
}

class PasswordException extends  Exception
{

}

验证代码片段 自行贴到 mvc框架控制器中:
 

 public function vp()
    {
//测试代码
        $password_list = array(
            "12dakedegss@#$", //含有123
            "dhdgafe98Dsw<", //合法
            "dedeRTdxbnvmaded",//没有数字
            "Ygd%s", //长度不够
            "你好YHDde092+",//非法字符
            "123Yhd345de#s",//含有123
            "GHJhj45%¥2sde",//三连GHJ
            "a^&*()_dmin78E%nihao",//含有admin
            "1Qw~!\_-’‘?、。,",
            "1Qw@#$%^&*()[]{}",
            "1Qw|:\"<>?,./;“”」",
            "1Qw「【】·`《》¥…;:",
            "Emicnet@?",
            "Emicnet@π1",
            "你好Emicnet1@",
            "Pdmin1+891",
        );

        foreach ($password_list as $password) {
            try {
                HelperPassword::validate($password);
            } catch (PasswordException $exception) {
                echo "$password is failed:".$exception->getMessage()."<br/>";
                continue;
            }
            echo $password."验证通过!"."<br/>";
        }

页面的输出:

12dakedegss@#$ is failed:密码不能含有两个连续相同的字符[s]
dhdgafe98Dsw<验证通过!
dedeRTdxbnvmaded is failed:密码必须同时含有大写、小写、数字和特殊字符
Ygd%s is failed:密码长度必须是8到20位
你好YHDde092+ is failed:密码含有系统不允许的特殊字符
123Yhd345de#s is failed:密码不能包括连续的3个字符键盘键位
GHJhj45%¥2sde is failed:密码不能包括连续的3个字符键盘键位
a^&*()_dmin78E%nihao验证通过!
1Qw~!\_-’‘?、。,验证通过!
1Qw@#$%^&*()[]{}验证通过!
1Qw|:"<>?,./;“”」验证通过!
1Qw「【】·`《》¥…;:验证通过!
Emicnet@? is failed:密码含有系统不允许的特殊字符
Pdmin1@π1 is failed:密码含有系统不允许的特殊字符
你好Pdmin1@ is failed:密码含有系统不允许的特殊字符
Pdmin1+891验证通过!

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值