Как проверить электронную почту, не прося пользователя войти в Laravel

Я разрабатываю приложение Laravel. Мое приложение использует встроенную функцию аутентификации Laravel. В аутентификации Laravel при регистрации пользователя отправляется письмо с подтверждением. Когда пользователь проверяет адрес электронной почты, щелкнув ссылку внутри сообщения электронной почты, пользователь должен снова войти в систему, чтобы подтвердить адрес электронной почты, если пользователь еще не вошел в систему.

VerificationController

class VerificationController extends Controller
{
    use VerifiesEmails, RedirectsUsersBasedOnRoles;

    /**
     * Create a new controller instance.
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('signed')->only('verify');
        $this->middleware('throttle:6,1')->only('verify', 'resend');
    }

    public function redirectPath()
    {
        return $this->getRedirectTo(Auth::guard()->user());
    }
}

Я пробовал комментировать эту строчку.

$this->middleware('auth');

Но он не работает, а вместо этого выдает ошибку. Как я могу включить Laravel, чтобы иметь возможность проверять электронную почту, даже если пользователь не вошел в систему?


person Wai Yan Hein    schedule 21.12.2018    source источник
comment
Почему вы хотите этого? Теперь я могу просто передать это электронное письмо и захватить любую учетную запись.   -  person Loek    schedule 21.12.2018
comment
Что делает RedirectsUsersBasedOnRoles? Когда вы говорите «Это не работает», что вы имеете в виду; вы получаете ошибку? Что за ошибка?   -  person Thomas Edwards    schedule 21.12.2018
comment
Он выдает ошибку, потому что ожидает экземпляр вашего пользователя. См .:   -  person adam    schedule 21.12.2018
comment
Спасибо, Адам. Спасибо, Лук, что указал на разумную причину. Ваше здоровье   -  person Wai Yan Hein    schedule 21.12.2018
comment
как так @Loek? URL подписан.   -  person AaronHS    schedule 15.08.2019


Ответы (7)


Сначала удалите строку $this->middleware('auth');, как вы это сделали.

Затем скопируйте метод verify из черты VerifiesEmails в свой VerificationController и немного измените его. Метод должен выглядеть так:

public function verify(Request $request)
{
    $user = User::find($request->route('id'));

    if (!hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) {
        throw new AuthorizationException;
    }

    if ($user->markEmailAsVerified())
        event(new Verified($user));

    return redirect($this->redirectPath())->with('verified', true);
}

Это переопределяет метод в трейте VerifiesUsers и удаляет проверку авторизации.

Безопасность (поправьте меня, если я ошибаюсь!)

Это по-прежнему безопасно, так как запрос подписан и проверен. Кто-то может проверить адрес электронной почты другого пользователя, если он каким-то образом получит доступ к проверочному электронному письму, но в 99% случаев это вряд ли вообще представляет собой риск.

person Wouter Florijn    schedule 15.01.2019
comment
Могу ли я использовать этот метод, не беспокоясь о проблемах безопасности? - person Ishaan; 08.08.2019
comment
Это было отредактировано для laravel 6? В предыдущих версиях ваше индивидуальное сравнение хешей не было обязательным, верно? - person Traxo; 23.02.2020
comment
Вызов неопределенного метода App \ User :: getEmailForVerification () - person Sead Lab; 12.05.2020
comment
@SeadLab убедитесь, что вы включили трейт MustVerifyEmail в модель User. Кроме этого, я не уверен, изменилось ли что-нибудь в Laravel 7. - person Wouter Florijn; 13.05.2020

Вот более перспективное решение проблемы:

class VerificationController extends Controller
{

    // …

    use VerifiesEmails {
        verify as originalVerify;
    }

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth'); // DON'T REMOVE THIS
        $this->middleware('signed')->only('verify');
        $this->middleware('throttle:6,1')->only('verify', 'resend');
    }

    /**
     * Mark the authenticated user's email address as verified.
     *
     * @param Request $request
     * @return Response
     *
     * @throws AuthorizationException
     */
    public function verify(Request $request)
    {
        $request->setUserResolver(function () use ($request) {
            return User::findOrFail($request->route('id'));
        });
        return $this->originalVerify($request);
    }
}

Поэтому, когда неаутентифицированный пользователь нажимает ссылку для подтверждения по электронной почте, происходит следующее:

  1. Пользователь будет перенаправлен на страницу входа 1
  2. Пользователь вводит учетные данные; успешно входит в систему 2
  3. Пользователь будет перенаправлен обратно на URL-адрес подтверждения электронной почты
  4. Электронная почта будет отмечена как подтвержденная

1 На этом этапе электронное письмо не будет помечено как подтвержденное.

2 Пользователь может вводить неверные учетные данные несколько раз. Как только он введет правильные учетные данные, он будет перенаправлен на предполагаемый URL-адрес подтверждения электронной почты.

person Martin    schedule 28.05.2020
comment
Однако трейт VerifyEmails был удален в Laravel 7.x. - person Nina Lisitsinskaya; 09.03.2021
comment
Я получаю, вы должны сначала подтвердить свой адрес электронной почты с этим. - person Gaurav; 12.03.2021

Не следует удалять $this->middleware('auth') полностью, так как это повлияет на перенаправления. Если вы удалите его, неаутентифицированные пользователи будут перенаправлены на / email / verify вместо / login.

поэтому $this->middleware('auth'); будет изменен на $this->middleware('auth')->except('verify'); в VerificationController

Также скопируйте функцию проверки из VerifyEmails в VerificationController

добавьте эти две строки кода вверху функции

$user = User::find($request->route('id'));

auth()->login($user);

поэтому вы программно входите в систему, а затем выполняете дальнейшие действия

person Junaid Masood    schedule 20.08.2020

Вот мой взгляд на ситуацию. Проверка требует, чтобы пользователь вошел в систему, прежде чем он сможет завершить проверку, поэтому мы можем переопределить функцию проверки и войти в систему, используя идентификатор, который мы получили в ссылке. Это безопасная причина, по которой функция проверки не вызывается, если Laravel не может проверить подпись из URL-адреса, поэтому, даже если кто-то изменит URL-адрес, он не сможет его обойти.

Перейдите в свой VerificationController и добавьте следующую функцию в конец файла.

public function verify(Request $request)
{
    if (!auth()->check()) {
        auth()->loginUsingId($request->route('id'));
    }

    if ($request->route('id') != $request->user()->getKey()) {
        throw new AuthorizationException;
    }

    if ($request->user()->hasVerifiedEmail()) {
        return redirect($this->redirectPath());
    }

    if ($request->user()->markEmailAsVerified()) {
        event(new Verified($request->user()));
    }

    return redirect($this->redirectPath())->with('verified', true);
}

Примечание

Убедитесь, что значение same_site в config / session.php установлено на lax. Если для него установлено значение «строгий», сеанс не будет сохраняться, если вы были перенаправлены с другого сайта. Например, если вы щелкнете ссылку подтверждения в Gmail, ваш файл cookie сеанса не будет сохраняться, поэтому он не будет перенаправлять вас на панель инструментов, но установит поле «email_verified_at» в базе данных, помечая проверку успешной. Пользователь не поймет, что произошло, потому что это перенаправит пользователя на страницу входа. Если вы установили для него значение «строгий», он будет работать, если вы скопируете ссылку для подтверждения прямо в адресную строку браузера, но не если пользователь щелкнет ссылку в веб-клиенте Gmail, поскольку он использует перенаправление для отслеживания ссылки.

person Ravi Patel    schedule 14.04.2021
comment
хорошая попытка. работает, как и ожидалось для меня. Благодарность - person Mohammad H.; 06.06.2021

если вы хотите активировать учетную запись пользователя без входа в систему, вы можете сделать это за 2 шага

1- Удалить или прокомментировать промежуточное ПО Auth в VerificationController

Пример ниже:

public function __construct()
{
    //$this->middleware('auth');
    $this->middleware('signed')->only('verify');
    $this->middleware('throttle:6,1')->only('verify', 'resend');
}

2- поскольку проверка маршрута проходит через {id}, вы можете просто отредактировать функцию проверки, чтобы найти пользователя по запросу идентификатора маршрута, как показано ниже:

путь к файлу: *: \ yourproject \ vendor \ laravel \ framework \ src \ Illuminate \ Foundation \ Auth \ VerifyEmails.php

$user = User::findOrfail($request->route('id'));

Полный пример

public function verify(Request $request)
{
    $user = User::findOrfail($request->route('id'));

    if (! hash_equals((string) $request->route('id'), (string) $user->getKey())) {
        throw new AuthorizationException;
    }

    if (! hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) {
        throw new AuthorizationException;
    }

    if ($user->hasVerifiedEmail()) {
        return redirect($this->redirectPath())->with('verified', true);
    }

    if ($user->markEmailAsVerified()) {
        event(new Verified($request->user()));
    }

    return redirect($this->redirectPath())->with('registered', true);
}
person Surf Junky    schedule 04.01.2020
comment
Привет, серфинг. Пожалуйста, подумайте о добавлении описания к вашему примеру кода. - person Eyk Rehbein; 04.01.2020

Решение для разрешения проверки электронной почты для пользователей, которые не вошли в систему (то есть без авторизации):

Изменения в: app / Http / Controllers / Auth / VerificationController.php:

  1. $this->middleware('auth'); to $this->middleware('auth')->except('verify');
  2. Скопируйте verify() метод из признака VerifiesEmails.
  3. Измените метод проверки, чтобы он работал без ожидаемых $request->user() данных.

Мой verify() метод в VerificationController выглядит так:

public function verify(\Illuminate\Http\Request $request)
{
    $user = User::find($request->route('id'));

    if ($request->route('id') != $user->getKey()) {
        throw new AuthorizationException;
    }

    if ($user->markEmailAsVerified())
        event(new Verified($user));

    return redirect()->route('login')->with('verified', true);
}

Подписанное промежуточное ПО

Laravel использует промежуточное ПО с именем signed для проверки целостности URL-адресов, сгенерированных приложением. Подписано проверяет, был ли изменен URL-адрес с момента его создания. Попробуйте изменить идентификатор, время истечения срока действия или подпись в URL-адресе, и это приведет к ошибке - очень эффективное и полезное промежуточное ПО для защиты метода verify ()

Для получения дополнительной информации: https://laravel.com/docs/8.x/urls#signed-urls

(По желанию)

Я перенаправил своих пользователей на маршрут входа в систему, а не на предполагаемый маршрут по двум причинам. 1) После входа в систему он будет пытаться перенаправить пользователя на ссылку для подтверждения адреса электронной почты, что приведет к ошибке; 2) Я хотел использовать проверенные истинные флэш-данные, которые были прикреплены к перенаправлению, чтобы отображать предупреждение на странице входа в систему, если пользователь успешно подтвердил свой адрес электронной почты.

Пример моего предупреждения на странице входа в систему:

@if(session()->has('verified'))
    <div class="alert alert-success">Your email address has been successfully verified.</div>
@endif

Предложения

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

person Darren Murphy    schedule 08.02.2021

person    schedule
comment
Бесполезно: точно такой же код, как в принятом ответе, и добавлен после последнего редактирования. - person Jan Żankowski; 27.05.2021