修改laravel5.3的密码验证

来源:互联网 发布:闲鱼淘宝二手手机 编辑:程序博客网 时间:2024/05/16 08:59

如何使用自定义的密码加密和验证方法?从网上找到了解决办法,现记录下来,一方面加深印象,另一方面写成blog备查。


从这篇文章可以了解laravel的认证流程:http://www.tuicool.com/articles/Av2aMb2。

按照文章中的办法,在app下新建文件夹hash,然后新建两个类文件:EloquentUserProvider.php和Security.php

Security.php内容:

namespace App\hash;use Illuminate\Contracts\Hashing\Hasher;/** * 加密类库,移植自Yii2.0的Security类 */class Security implements Hasher{       public $passwordHashCost = 13;    private $_useLibreSSL;    private $_randomFile;    /**     * Generates specified number of random bytes.     * Note that output may not be ASCII.     * @see generateRandomString() if you need a string.     *     * @param integer $length the number of bytes to generate     * @return string the generated random bytes     * @throws InvalidParamException if wrong length is specified     * @throws Exception on failure.     */    public function generateRandomKey($length = 32)    {        if (!is_int($length)) {            throw new Exception('First parameter ($length) must be an integer');        }        if ($length < 1) {            throw new Exception('First parameter ($length) must be greater than 0');        }        // always use random_bytes() if it is available        if (function_exists('random_bytes')) {            return random_bytes($length);        }        // The recent LibreSSL RNGs are faster and likely better than /dev/urandom.        // Parse OPENSSL_VERSION_TEXT because OPENSSL_VERSION_NUMBER is no use for LibreSSL.        // https://bugs.php.net/bug.php?id=71143        if ($this->_useLibreSSL === null) {            $this->_useLibreSSL = defined('OPENSSL_VERSION_TEXT')                && preg_match('{^LibreSSL (\d\d?)\.(\d\d?)\.(\d\d?)$}', OPENSSL_VERSION_TEXT, $matches)                && (10000 * $matches[1]) + (100 * $matches[2]) + $matches[3] >= 20105;        }        // Since 5.4.0, openssl_random_pseudo_bytes() reads from CryptGenRandom on Windows instead        // of using OpenSSL library. LibreSSL is OK everywhere but don't use OpenSSL on non-Windows.        if ($this->_useLibreSSL            || (                DIRECTORY_SEPARATOR !== '/'                && substr_compare(PHP_OS, 'win', 0, 3, true) === 0                && function_exists('openssl_random_pseudo_bytes')            )        ) {            $key = openssl_random_pseudo_bytes($length, $cryptoStrong);            if ($cryptoStrong === false) {                throw new Exception(                    'openssl_random_pseudo_bytes() set $crypto_strong false. Your PHP setup is insecure.'                );            }            if ($key !== false && StringHelper::byteLength($key) === $length) {                return $key;            }        }        // mcrypt_create_iv() does not use libmcrypt. Since PHP 5.3.7 it directly reads        // CryptGenRandom on Windows. Elsewhere it directly reads /dev/urandom.        if (function_exists('mcrypt_create_iv')) {            $key = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);            if (StringHelper::byteLength($key) === $length) {                return $key;            }        }        // If not on Windows, try to open a random device.        if ($this->_randomFile === null && DIRECTORY_SEPARATOR === '/') {            // urandom is a symlink to random on FreeBSD.            $device = PHP_OS === 'FreeBSD' ? '/dev/random' : '/dev/urandom';            // Check random device for special character device protection mode. Use lstat()            // instead of stat() in case an attacker arranges a symlink to a fake device.            $lstat = @lstat($device);            if ($lstat !== false && ($lstat['mode'] & 0170000) === 020000) {                $this->_randomFile = fopen($device, 'rb') ?: null;                if (is_resource($this->_randomFile)) {                    // Reduce PHP stream buffer from default 8192 bytes to optimize data                    // transfer from the random device for smaller values of $length.                    // This also helps to keep future randoms out of user memory space.                    $bufferSize = 8;                    if (function_exists('stream_set_read_buffer')) {                        stream_set_read_buffer($this->_randomFile, $bufferSize);                    }                    // stream_set_read_buffer() isn't implemented on HHVM                    if (function_exists('stream_set_chunk_size')) {                        stream_set_chunk_size($this->_randomFile, $bufferSize);                    }                }            }        }        if (is_resource($this->_randomFile)) {            $buffer = '';            $stillNeed = $length;            while ($stillNeed > 0) {                $someBytes = fread($this->_randomFile, $stillNeed);                if ($someBytes === false) {                    break;                }                $buffer .= $someBytes;                $stillNeed -= StringHelper::byteLength($someBytes);                if ($stillNeed === 0) {                    // Leaving file pointer open in order to make next generation faster by reusing it.                    return $buffer;                }            }            fclose($this->_randomFile);            $this->_randomFile = null;        }        throw new Exception('Unable to generate a random key');    }    /**     * Generates a random string of specified length.     * The string generated matches [A-Za-z0-9_-]+ and is transparent to URL-encoding.     *     * @param integer $length the length of the key in characters     * @return string the generated random key     * @throws Exception on failure.     */    public function generateRandomString($length = 32)    {        if (!is_int($length)) {            throw new Exception('First parameter ($length) must be an integer');        }        if ($length < 1) {            throw new Exception('First parameter ($length) must be greater than 0');        }        $bytes = $this->generateRandomKey($length);        // '=' character(s) returned by base64_encode() are always discarded because        // they are guaranteed to be after position $length in the base64_encode() output.        return strtr(substr(base64_encode($bytes), 0, $length), '+/', '_-');    }    /**     * Generates a secure hash from a password and a random salt.     *     * The generated hash can be stored in database.     * Later when a password needs to be validated, the hash can be fetched and passed     * to [[validatePassword()]]. For example,     *     * ```php     * // generates the hash (usually done during user registration or when the password is changed)     * $hash = Yii::$app->getSecurity()->generatePasswordHash($password);     * // ...save $hash in database...     *     * // during login, validate if the password entered is correct using $hash fetched from database     * if (Yii::$app->getSecurity()->validatePassword($password, $hash) {     *     // password is good     * } else {     *     // password is bad     * }     * ```     *     * @param string $password The password to be hashed.     * @param integer $cost Cost parameter used by the Blowfish hash algorithm.     * The higher the value of cost,     * the longer it takes to generate the hash and to verify a password against it. Higher cost     * therefore slows down a brute-force attack. For best protection against brute-force attacks,     * set it to the highest value that is tolerable on production servers. The time taken to     * compute the hash doubles for every increment by one of $cost.     * @return string The password hash string. When [[passwordHashStrategy]] is set to 'crypt',     * the output is always 60 ASCII characters, when set to 'password_hash' the output length     * might increase in future versions of PHP (http://php.net/manual/en/function.password-hash.php)     * @throws Exception on bad password parameter or cost parameter.     * @see validatePassword()     */    public function generatePasswordHash($password, $cost = null)    {        if ($cost === null) {            $cost = $this->passwordHashCost;        }        if (function_exists('password_hash')) {            /** @noinspection PhpUndefinedConstantInspection */            return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);        }        $salt = $this->generateSalt($cost);        $hash = crypt($password, $salt);        // strlen() is safe since crypt() returns only ascii        if (!is_string($hash) || strlen($hash) !== 60) {            throw new Exception('Unknown error occurred while generating hash.');        }        return $hash;    }    /**     * Verifies a password against a hash.     * @param string $password The password to verify.     * @param string $hash The hash to verify the password against.     * @return boolean whether the password is correct.     * @throws InvalidParamException on bad password/hash parameters or if crypt() with Blowfish hash is not available.     * @see generatePasswordHash()     */    public function validatePassword($password, $hash)    {        if (!is_string($password) || $password === '') {            throw new Exception('Password must be a string and cannot be empty.');        }        if (!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hash, $matches)            || $matches[1] < 4            || $matches[1] > 30        ) {            throw new Exception('Hash is invalid.');        }        if (function_exists('password_verify')) {            return password_verify($password, $hash);        }        $test = crypt($password, $hash);        $n = strlen($test);        if ($n !== 60) {            return false;        }        return $this->compareString($test, $hash);    }    /**     * Generates a salt that can be used to generate a password hash.     *     * The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function     * requires, for the Blowfish hash algorithm, a salt string in a specific format:     * "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters     * from the alphabet "./0-9A-Za-z".     *     * @param integer $cost the cost parameter     * @return string the random salt value.     * @throws InvalidParamException if the cost parameter is out of the range of 4 to 31.     */    protected function generateSalt($cost = 13)    {        $cost = (int) $cost;        if ($cost < 4 || $cost > 31) {            throw new Exception('Cost must be between 4 and 31.');        }        // Get a 20-byte random string        $rand = $this->generateRandomKey(20);        // Form the prefix that specifies Blowfish (bcrypt) algorithm and cost parameter.        $salt = sprintf("$2y$%02d$", $cost);        // Append the random salt data in the required base64 format.        $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));        return $salt;    }    /**     * Performs string comparison using timing attack resistant approach.     * @see http://codereview.stackexchange.com/questions/13512     * @param string $expected string to compare.     * @param string $actual user-supplied string.     * @return boolean whether strings are equal.     */    public function compareString($expected, $actual)    {        $expected .= "\0";        $actual .= "\0";        $expectedLength = StringHelper::byteLength($expected);        $actualLength = StringHelper::byteLength($actual);        $diff = $expectedLength - $actualLength;        for ($i = 0; $i < $actualLength; $i++) {            $diff |= (ord($actual[$i]) ^ ord($expected[$i % $expectedLength]));        }        return $diff === 0;    }    public function make($value, array $options = [])    {        return $this->generatePasswordHash($value);    }    public function check($password, $hashedValue, array $options = [])    {        if (!is_string($password) || $password === '') {            throw new Exception('Password must be a string and cannot be empty.');        }        if (!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hashedValue['password'], $matches)            || $matches[1] < 4            || $matches[1] > 30        ) {            throw new Exception('Hash is invalid.');        }        if (function_exists('password_verify')) {                        return password_verify($password, $hashedValue['password']);        }        $test = crypt($password, $hashedValue['password']);        $n = strlen($test);        if ($n !== 60) {            return false;        }        return $this->compareString($test, $hashedValue['password']);    }    public function needsRehash($hashedValue, array $options = [])    {        return false;    }}

类中实现了3个方法:make -- 加密密码,check -- 校验密码,needsRehash -- 暂不支持。

新注册用户时,将会调用make方法生成加密的密码;用户登录时,调用check校验密码。


EloquentUserProvider.php内容:

<?phpnamespace App\hash;use Illuminate\Auth\EloquentUserProvider;use Illuminate\Contracts\Auth\Authenticatable;use Illuminate\Support\Str;class EloquentUserProvider extends EloquentUserProvider{/** * Validate a user against the given credentials. * * @param \Illuminate\Contracts\Auth\Authenticatable $user * @param array $credentials * @return bool */public function validateCredentials(Authenticatable $user, array $credentials){$plain = $credentials['password'];$authPassword = $user->getAuthPassword();//return bcrypt($plain) === $authPassword['password'];return $this->hasher->check($plain, $authPassword);}}

类中实现了一个方法:validateCredentials -- 校验用户密码


然后需要修改User类,添加getAuthPassword方法:

public function getAuthPassword()    {        return ['password' => $this->attributes['password_hash']];    }


使用命令行:

php artisan make:provider HashServiceProvider

此命令会在app/Providers下创建文件:HashServiceProvider.php,打开这个文件,在register方法中添加下面代码:

$this->app['hash'] = $this->app->share(function() {    return new \App\hash\Security();});
上面的代码注册新的hash类为刚才的Security类。

修改config/app.php文件引用适配器:

//注释掉这一行//Illuminate\Hashing\HashServiceProvider::class,//添加下面这一行App\Providers\HashServiceProvider::class,

做完上述工作后,需要修改laravel的Auth适配器为我们自己的适配器:

打开app/Providers/AuthServiceProvider.php,修改boot方法:

public function boot()    {        $this->registerPolicies();        \Auth::provider('my-eloquent', function($app, $config) {            return new \App\hash\EloquentUserProvider($this->app['hash'], $config['model']);        });    }
打开config/auth.php,修改providers数组内容:

'users' => [            'driver' => 'my-eloquent',            'model' => App\User::class,        ],


至此,修改完毕,经测试注册和登录验证均正常。



0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 余额宝超10万怎么办 商场主题经营改变商户怎么办 一个好的项目需要资金怎么办 没有做暂估入库的凭证怎么办 电脑显示宽带连接已断开怎么办 电脑ip地址连不上网怎么办 百度网盘资源打不开怎么办 百度网盘视频格式不支持怎么办 origin注册邮箱填错了怎么办 58同城手机输入不合法怎么办 银行卡密码输入错误三次怎么办 私密相册系统升级后打不开怎么办 由于志愿没填好孩子没书读怎么办 文具店不开了货怎么办 华为手机通讯录联系人重复怎么办 vcf文件用表格打开乱码怎么办 表格打出来太小怎么办 企业列入经营异常名录怎么办 小米电视滚动字幕模糊怎么办 海信电视浑的看不清怎么办 电视打开特别暗看不清怎么办 诈骗电话按了键怎么办 上海油电混合送沪牌以后怎么办? 车子被前夫砸了怎么办 老的标书丢了怎么办 拍牌照的标书掉了怎么办 上海拍到车牌后怎么办 杭州4s店车牌怎么办 天津车牌有指标想买车怎么办 上海大牌拍中了怎么办 买新车牌下不了怎么办 临沂上小学没报上名怎么办 早教中心倒闭了怎么办 企业税没交联系不上法人怎么办 企业被拉黑法人联系不到怎么办 支票根写错了怎么办 月结客户不付款怎么办 退市整理期过了怎么办 老板跑了财务负责人怎么办 在南京加入嘀嘀代驾怎么办 苹果盗刷支付宝怎么办