【问题标题】:Allow multiple password reset tokens in Laravel在 Laravel 中允许多个密码重置令牌
【发布时间】:2019-10-12 00:01:16
【问题描述】:

Laravel (5.7) 的密码重置系统的默认行为是在为该用户删除任何其他人之后在password_resets 表中创建一个新令牌。此行为在 \Illuminate\Auth\Passwords\DatabaseTokenRepository 中确定,并且似乎不可配置。

protected function deleteExisting(CanResetPasswordContract $user)
{
    return $this->getTable()->where('email', $user->getEmailForPasswordReset())->delete();
}

继承太多了,我不知道要扩展哪些类,所以我可以插入自己的规则。

是否可以在不侵入 Laravel 核心文件的情况下允许同时存在一定数量的密码重置?我需要扩展哪些类?

【问题讨论】:

    标签: php laravel laravel-authentication


    【解决方案1】:

    提供的答案并没有帮助我覆盖正确的类,但它确实给了我一些如何解决这个问题的想法。所以我最终创建了三个类,它们都扩展了内置类:

    DatabaseTokenRepository

    这是我从the parent class 进行覆盖以允许我的自定义行为的地方;创建新的重置令牌时保留最近的两个条目,并在执行重置时检查多个令牌。

    <?php
    
    namespace App\Services;
    
    use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
    use Illuminate\Auth\Passwords\DatabaseTokenRepository as DatabaseTokenRepositoryBase;
    
    class DatabaseTokenRepository extends DatabaseTokenRepositoryBase
    {
        /**
         * Create a new token record.
         *
         * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
         * @return string
         */
        public function create(CanResetPasswordContract $user)
        {
            $email = $user->getEmailForPasswordReset();
    
            $this->deleteSomeExisting($user);
    
            // We will create a new, random token for the user so that we can e-mail them
            // a safe link to the password reset form. Then we will insert a record in
            // the database so that we can verify the token within the actual reset.
            $token = $this->createNewToken();
    
            $this->getTable()->insert($this->getPayload($email, $token));
    
            return $token;
        }
    
        /**
         * Determine if a token record exists and is valid.
         *
         * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
         * @param  string  $token
         * @return bool
         */
        public function exists(CanResetPasswordContract $user, $token)
        {
            $records = $this->getTable()
                ->where("email", $user->getEmailForPasswordReset())
                ->get();
    
            foreach ($records as $record) {
                if (
                   ! $this->tokenExpired($record->created_at) &&
                     $this->hasher->check($token, $record->token)
                ) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * Delete SOME existing reset tokens from the database.
         *
         * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
         * @return int
         */
        protected function deleteSomeExisting($user)
        {
            // TODO: make this configurable in app config
            $limit = 3;
            $records = $this->getTable()
                ->where("email", $user->getEmailForPasswordReset())
                ->orderBy("created_at");
            $ct = $records->count() - $limit + 1;
            return ($ct > 0) ? $records->limit($ct)->delete() : 0;
        }
    }
    

    PasswordBrokerManager

    这只是确保使用我上面的自定义存储库类。该函数完全从the parent class 复制而来,但当然位于不同的命名空间中。

    <?php
    
    namespace App\Services;
    
    use Illuminate\Support\Str;
    use Illuminate\Auth\Passwords\PasswordBrokerManager as PasswordBrokerManagerBase;
    
    class PasswordBrokerManager extends PasswordBrokerManagerBase
    {
        /**
         * Create a token repository instance based on the given configuration.
         *
         * @param  array  $config
         * @return \Illuminate\Auth\Passwords\TokenRepositoryInterface
         */
        protected function createTokenRepository(array $config)
        {
            $key = $this->app['config']['app.key'];
    
            if (Str::startsWith($key, 'base64:')) {
                $key = base64_decode(substr($key, 7));
            }
    
            $connection = $config['connection'] ?? null;
    
            return new DatabaseTokenRepository(
                $this->app['db']->connection($connection),
                $this->app['hash'],
                $config['table'],
                $key,
                $config['expire']
            );
        }
    }
    

    PasswordResetServiceProvider

    同样,只需确保返回自定义类。同样,只有命名空间从 the original 更改。

    <?php
    
    namespace App\Providers;
    
    use App\Services\PasswordBrokerManager;
    use Illuminate\Auth\Passwords\PasswordResetServiceProvider as PasswordResetServiceProviderBase;
    
    class PasswordResetServiceProvider extends PasswordResetServiceProviderBase
    {
        /**
         * Register the password broker instance.
         *
         * @return void
         */
        protected function registerPasswordBroker()
        {
            $this->app->singleton("auth.password", function ($app) {
                return new PasswordBrokerManager($app);
            });
    
            $this->app->bind("auth.password.broker", function ($app) {
                return $app->make("auth.password")->broker();
            });
        }
    }
    

    最后,应用程序配置更新为使用我的提供程序而不是原来的:

        // Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        App\Providers\PasswordResetServiceProvider::class,
    

    一切都很顺利。

    【讨论】:

    • 非常感谢您发布您的解决方案!在尝试实施基于用户名的密码重置时,我发现自己被困在同一个地方。你让我摆脱了困境,谢谢。
    【解决方案2】:

    复制自https://www.5balloons.info/extending-passwordbroker-class-laravel-5/

    App\Providers 内创建一个CustomPasswordResetServiceProvider

    您需要创建一个new CustomPasswordResetServiceProvider 类,我们将使用它来替换默认的PasswordResetServiceProvider

    namespace App\Providers;
    use Illuminate\Support\ServiceProvider;
    use App\Services\CustomPasswordBrokerManager; 
    class CustomPasswordResetServiceProvider extends ServiceProvider{
        protected $defer = true;
    
        public function register()
        {
            $this->registerPasswordBrokerManager();
        }
    
        protected function registerPasswordBrokerManager()
        {
            $this->app->singleton('auth.password', function ($app) {
                return new CustomPasswordBrokerManager($app);
            });
        }
    
        public function provides()
        {
            return ['auth.password'];
        }
    }
    

    更换app/config.php中的服务商

    //Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
    
    App\Providers\CustomPasswordResetServiceProvider::class,
    

    创建新的CustomPasswordBrokerManager

    App/Services目录下新建一个类CustomPasswordBrokerManager,并复制Illuminate\Auth\Passwords\PasswordBrokerManager.phpPasswordBrokerManager的所有内容

    然后修改函数resolve以返回我的CustomPasswordProvider类的实例

    protected function resolve($name)
    {
        $config = $this->getConfig($name);
        if (is_null($config)) {
            throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
        }
    
        return new CustomPasswordBroker(
            $this->createTokenRepository($config),
            $this->app['auth']->createUserProvider($config['provider'])
    );
    }
    

    创建 CustomPasswordBroker

    最后,您现在可以在App/Services 目录下创建新的CustomPasswordBroker 类,该目录扩展了位于Illuminate\Auth\Passwords\PasswordBroker 的默认PasswordBroker

    use Illuminate\Auth\Passwords\PasswordBroker as BasePasswordBroker;    
    
    class CustomPasswordBroker extends BasePasswordBroker    
    {    
    // override the functions that you need here    
    }
    

    【讨论】:

    • \Illuminate\Auth\Passwords\DatabaseTokenRepository 是我需要重写的类; Illuminate\Auth\Passwords\PasswordBroker::$tokens 是此类的一个实例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-21
    • 2018-09-30
    • 2019-05-02
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    相关资源
    最近更新 更多