首页 文章

使用Laravel 5.2的内置身份验证将旧的md5密码迁移到bcrypt

提问于
浏览
4

我正在将一个旧的PHP应用程序迁移到Laravel 5.2 . 该应用程序有一个巨大的用户表(约50K用户),密码都是MD5哈希 .

显然这是不可接受的,而不是向所有50,000名用户发送电子邮件,要求他们重置密码,我想在后台将密码更改为bcrypt哈希 .

为此,我想在其中创建一个带有MD5哈希的 old_password 列,然后每当用户登录时,我都会根据MD5哈希(如果存在)检查密码,然后下次创建一个新的bcrypt哈希,删除MD5哈希 .

我已经看到了一些关于如何执行此操作的示例(例如thisthis),但没有专门用于Laravel 5,也没有专门用于Laravel 5.2的内置身份验证 .

有没有一种干净的方法来调整内置的auth来做到这一点,或者我最好在这种情况下编写自己的手动auth系统?

4 回答

  • 0

    从Drupal迁移时我遇到了类似的问题 . 我没有为旧密码创建一个新列,但更新了hasher以检查密码Drupal-way然后如果失败,请使用bcrypt进行检查 . 这样老用户可以以与新用户相同的方式登录 .

    您需要在app中的任何位置创建一个包,比如app / packages / hashing . 把这两个文件放在那里 .

    YourHashingServiceProvider.php

    <?php namespace App\Packages\Hashing;
    
    use Illuminate\Support\ServiceProvider;
    
    class YourHashingServiceProvider extends ServiceProvider {
    
        /**
         * Indicates if loading of the provider is deferred.
         *
         * @var bool
         */
        protected $defer = true;
    
        /**
         * Register the service provider.
         *
         * @return void
         */
        public function register()
        {
            $this->app->singleton('hash', function() { return new YourHasher; });
        }
    
        /**
         * Get the services provided by the provider.
         *
         * @return array
         */
        public function provides()
        {
            return ['hash'];
        }
    
    }
    

    YourHasher.php

    <?php namespace App\Packages\Hashing;
    
    use Illuminate\Contracts\Hashing\Hasher as HasherContract;
    use Illuminate\Hashing\BcryptHasher;
    use Auth;
    
    class YourHasher implements HasherContract
    {
    
        protected $hasher;
    
        /**
         * Create a new Sha512 hasher instance.
         */
        public function __construct()
        {
            $this->hasher = new BcryptHasher;
        }
    
        /**
         * Hash the given value.
         *
         * @param string $value
         * @param array  $options
         *
         * @return string
         */
        public function make($value, array $options = [])
        {
            return $this->hasher->make($value, $options);
        }
    
        /**
         * Check the given plain value against a hash.
         *
         * @param  string $value
         * @param  string $hashedValue
         * @param  array  $options
         *
         * @return bool
         */
        public function check($value, $hashedValue, array $options = [])
        {
            return md5($value) == $hashedValue || $this->hasher->check($value, $hashedValue, $options);
        }
    
        /**
         * Check if the given hash has been hashed using the given options.
         *
         * @param  string $hashedValue
         * @param  array  $options
         *
         * @return bool
         */
        public function needsRehash($hashedValue, array $options = [])
        {
            return substr($hashedValue, 0, 4) != '$2y$';
        }
    }
    

    然后将 App\Packages\Hashing\YourHashingServiceProvider::class 放在 providers 中的config / app.class中 . 此时,您的旧用户应该能够登录您的laravel应用程序 .

    现在,要在用户控制器(登录/注册表单)中的某个位置更新其密码,您可以使用 Hash::needsRehash($hashed)Hash::make($password_value) 为用户生成新的bcrypt密码,然后保存 .

  • 1

    在Laravel 5.2中,您的 AuthController.php 应该覆盖登录方法,只需添加以下内容即可 .

    登录失败时,它会尝试使用md5()登录用户 .

    public function login(Request $request)
    {
    
        $this->validateLogin($request);
    
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        $throttles = $this->isUsingThrottlesLoginsTrait();
    
        if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
    
            return $this->sendLockoutResponse($request);
        }
    
        $credentials = $this->getCredentials($request);
    
    
        if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
            return $this->handleUserWasAuthenticated($request, $throttles);
        }
    
        //If user got here it means the AUTH was unsuccessful
        //Try to log them IN using MD5
        if($user = User::whereEmail($credentials['email'])->wherePassword(md5($credentials['password']))->first()){
            //It this condition is true, the user had the right password.
    
            //encrypt the password using bcrypt
            $user->password     = bcrypt($credentials['password']);
            $user->save();
    
            if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
                return $this->handleUserWasAuthenticated($request, $throttles);
            }
    
            return $this->handleUserWasAuthenticated($request, $throttles);
    
        }
    
    
    
        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        if ($throttles && ! $lockedOut) {
            $this->incrementLoginAttempts($request);
        }
    
        return $this->sendFailedLoginResponse($request);
    }
    
  • 5

    基于我在sustainable password hashing上读到的一篇文章,我对解决方案的解决方法与@neochief略有不同,特别是元算法的底部位 .

    我分3步完成了这个步骤:

    • 通过将md5密码包装在bcrypt中,将bcrypt应用于数据库中的所有用户密码,就好像它们是纯文本一样

    • 当用户尝试进行身份验证时,使用 guard->attempt(...) 单独使用bcrypt . 如果身份验证失败,则使用md5对请求中发送的密码进行双重加密,然后尝试使用 guard->attempt(...) 重新进行身份验证,然后将md5包装在bcrypt中进行比较 .

    • 经过身份验证后,只使用bcrypt存储纯文本密码,因此双重加密不必同时应用于同一用户 .

    我将AuthenticatesUsers :: login引入AuthController以使用我自己的逻辑覆盖逻辑,并调用一个包含登录尝试逻辑的受保护方法 . 我正在使用JWT-Auth,但如果你不是你的解决方案将没有太大的不同 .

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);
    
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        $throttles = $this->isUsingThrottlesLoginsTrait();
    
        if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
    
            return $this->sendLockoutResponse($request);
        }
    
        $credentials = $this->getCredentials($request);
    
        if ($token = $this->authenticate($credentials)) {
            return $this->handleUserWasAuthenticated($request, $throttles, $token);
        }
    
        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        if ($throttles && !$lockedOut) {
            $this->incrementLoginAttempts($request);
        }
    
        return $this->sendFailedLoginResponse($request);
    }
    
    /**
     * Authentication using sustainable password encryption that allows for updates to the
     * applications hash strategy that employs modern security requirements.
     * ---
     * IMPORTANT: The meta-algorithm strategy assumes that all existing passwords that use
     * an obsolete security standard for encryption have been further encrypted with an
     * up-to-date modern security standard.
     * ---
     * NOTE: Mutator has been applied to User model to store any passwords
     * that are saved using a standard for modern encryption.
     *
     * @param $credentials
     * @return string|bool
     */
    protected function authenticate($credentials)
    {
        // Attempt to authenticate using modern security standards
        $token = Auth::guard($this->getGuard())->attempt($credentials);
    
        // If the authentication failed, re-attempt using obsolete password encryption
        // to wrap the plain-text password from the request
        if ($token === false) {
    
            // Make a copy of the plain-text password
            $password = $credentials['password'];
    
            // Apply obsolete password encryption to plain-text password
            $credentials['password'] = md5($password);
    
            // Re-attempt authentication
            $token = Auth::guard($this->getGuard())->attempt($credentials);
    
            if ($token) {
    
                // Store password using modern security standard
                $user = Auth::user();
                $user->password = $password;
                $user->save();
            }
        }
    
        return $token;
    }
    

    希望这对某人有用 .

  • 2

    我将@Leonardo Beal的AuthController更新为Laravel 5.6 .

    我将我的应用程序从Laravel 4.2迁移到5.6,这就像一个魅力(添加到app / Http / Controllers / Auth / LoginController.php):

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);
    
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
    
            return $this->sendLockoutResponse($request);
        }
    
        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }
    
        //If user got here it means the AUTH was unsuccessful
        //Try to log them IN using MD5
        if ($user = User::whereEmail($request->input('email'))
            ->wherePassword(md5($request->input('password')))->first()) {
            //It this condition is true, the user had the right password.
    
            //encrypt the password using bcrypt
            $user->password = bcrypt($request->input('password'));
            $user->save();
    
            $this->validateLogin($request);
    
            if ($this->hasTooManyLoginAttempts($request)) {
                $this->fireLockoutEvent($request);
    
                return $this->sendLockoutResponse($request);
            }
    
            if ($this->attemptLogin($request)) {
                return $this->sendLoginResponse($request);
            }
        }
    
        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);
    
        return $this->sendFailedLoginResponse($request);
    }
    

相关问题