Сброс пароля в Laravel по электронной почте или мобильному телефону

По умолчанию система сброса пароля Laravel 5.5 работает с электронной почтой, но мне нужно добавить поддержку мобильного номера (проверить с помощью OTP, сгенерировать токен и перенаправить на страницу сброса пароля). Я делаю всю эту часть и создал мобильный столбец в таблице password_resets.

Но проблема в \Illuminate\Auth\Passwords\DatabaseTokenRepository && \Illuminate\Auth\Passwords\TokenRepositoryInterface ON exist методе, который не кажется настраиваемым.

public function exists(CanResetPasswordContract $user, $token)
{
    $record = (array) $this->getTable()->where(
        'email', $user->getEmailForPasswordReset()
    )->first();

    return $record &&
           ! $this->tokenExpired($record['created_at']) &&
             $this->hasher->check($token, $record['token']);
}

Мне нужно переопределить этот метод. Так много наследства. Какие классы мне нужно расширить и как переопределить этот метод.


person Hasan Hafiz Pasha    schedule 16.11.2019    source источник
comment
у вашего пользователя нет адреса электронной почты? или вы говорите, что ссылаетесь на пользователя только по номеру телефона в таблице password_resets   -  person lagbox    schedule 16.11.2019
comment
Я не знаю, можно ли прикоснуться к этим классам. Я бы ввел новую защиту для аутентификации с использованием номера телефона и переопределил метод, который отправляет уведомление в модели пользователя, документация   -  person nakov    schedule 16.11.2019
comment
@lagbox - да, у нашего пользователя есть адрес электронной почты, но это необязательно. Номер мобильного телефона является обязательным. Итак, мы добавляем мобильный столбец в таблицу сброса пароля.   -  person Hasan Hafiz Pasha    schedule 16.11.2019
comment
@nakov, я думаю, эта ветка полезна - stackoverflow.com/questions/56589029/. Я слежу за этим, но я думаю, что скучаю по некоторым вещам   -  person Hasan Hafiz Pasha    schedule 16.11.2019
comment
@HasanHafizPasha, вы можете прокомментировать пост и узнать, может ли парень, который реализовал это, помочь вам больше.   -  person nakov    schedule 16.11.2019


Ответы (1)


Если вы хотите переопределить поведение \Illuminate\Auth\Passwords\DatabaseTokenRepository методов, вам придется создать собственное хранилище токенов.

Раньше вы могли переопределять методы в существующем репозитории, но теперь это protected. К сожалению, это означает в основном копирование и вставку всего кода репозитория из vendor/laravel/framework/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php.

Создайте эти файлы:

приложение / Auth / DatabaseTokenRepository.php

<?php

namespace App\Auth;

use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

class DatabaseTokenRepository implements TokenRepositoryInterface
{
    //
    // Override these methods
    //
    public function __construct(
        protected ConnectionInterface $connection,
        protected HasherContract $hasher,
        protected string $table
        protected string $hashKey,
        protected int $expires = 60,
        protected int $throttle = 60
    )
    {
        // PHP 8 constructor promotion, not much code here!
        $this->expires = $expires * 60;
    }

    public function create(CanResetPasswordContract $user)
    {
        $email = $user->getEmailForPasswordReset();
        $mobile = $user->getMobileForPasswordReset();
        $this->deleteExisting($user);
        $token = $this->createNewToken();
        $this->getTable()->insert($this->getPayload($email, $mobile, $token));
        return $token;
    }

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

    protected function getPayload($email, $mobile, $token): array
    {
        return [
            "email" => $email,
            "mobile" => $mobile,
            "token" => $this->hasher->make($token),
            "created_at" => new Carbon(),
        ];
    }

    public function exists(CanResetPasswordContract $user, $token)
    {
        $record = (array) $this->getTable()
            ->where("email", $user->getEmailForPasswordReset())
            ->orWhere("mobile", $user->getMobileForPasswordReset())
            ->first();
        return $record &&
               ! $this->tokenExpired($record["created_at"]) &&
                 $this->hasher->check($token, $record["token"]);
    }

    public function recentlyCreatedToken(CanResetPasswordContract $user)
    {
        $record = (array) $this->getTable()
            ->where("email", $user->getEmailForPasswordReset())
            ->orWhere("mobile", $user->getMobileForPasswordReset())
            ->first();

        return $record && $this->tokenRecentlyCreated($record['created_at']);
    }

    //
    // Remaining methods are untouched
    //
    protected function tokenExpired($createdAt)
    {
        return Carbon::parse($createdAt)->addSeconds($this->expires)->isPast();
    }

    protected function tokenRecentlyCreated($createdAt)
    {
        if ($this->throttle <= 0) {
            return false;
        }

        return Carbon::parse($createdAt)->addSeconds(
            $this->throttle
        )->isFuture();
    }

    public function delete(CanResetPasswordContract $user)
    {
        $this->deleteExisting($user);
    }

    public function deleteExpired()
    {
        $expiredAt = Carbon::now()->subSeconds($this->expires);

        $this->getTable()->where('created_at', '<', $expiredAt)->delete();
    }

    public function createNewToken()
    {
        return hash_hmac('sha256', Str::random(40), $this->hashKey);
    }

    public function getConnection()
    {
        return $this->connection;
    }

    protected function getTable()
    {
        return $this->connection->table($this->table);
    }

    public function getHasher()
    {
        return $this->hasher;
    }
}

Теперь вам нужно будет использовать этот собственный репозиторий токенов вместо репозитория по умолчанию. Поэтому вам нужно переопределить другой класс.

app / Auth / PasswordBrokerManager.php

<?php
namespace App\Auth;

use Illuminate\Support\Str;
use Illuminate\Auth\Passwords\PasswordBrokerManager as PasswordBrokerManagerBase;

class PasswordBrokerManager extends PasswordBrokerManagerBase
{
    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 an instance of your new repository
        // it's in the same namespace, no need to alias it
        return new DatabaseTokenRepository(
            $this->app['db']->connection($connection),
            $this->app['hash'],
            $config['table'],
            $key,
            $config['expire']
        );
    }
}

Теперь вы создали настраиваемого брокера для использования своего настраиваемого репозитория. Чтобы им воспользоваться, вам нужен новый поставщик услуг.

app / Providers / PasswordResetServiceProvider.php

<?php
namespace App\Providers;

use App\Auth\PasswordBrokerManager;
use Illuminate\Auth\Passwords\PasswordResetServiceProvider as PasswordResetServiceProviderBase;

class PasswordResetServiceProvider extends PasswordResetServiceProviderBase
{
    protected function registerPasswordBroker()
    {
        $this->app->singleton('auth.password', function ($app) {
            // reference your new broker
            // the rest of the method code is unchanged
            return new PasswordBrokerManager($app);
        });
        $this->app->bind('auth.password.broker', function ($app) {
            return $app->make('auth.password')->broker();
        });
    }
}

Затем замените поставщика услуг сброса пароля по умолчанию на другого в конфигурации вашего приложения: app / config / app.php

<?php

return [
    "providers" => [
        ...
        // Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        App\Providers\PasswordResetServiceProvider::class,
        ...
    ],
];

И, наконец, определите метод getMobileForPasswordReset() в своей пользовательской модели:

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
...

class User extends Authenticatable
{
    ...
    public method getMobileForPasswordReset()
    {
        return $this->mobile;
    }
}
person miken32    schedule 16.11.2019