Как проверить электронную почту, не спрашивая пользователя войти в 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 для проверки электронной почты, даже если пользователь не вошел в систему?
10 ответов
Сначала удалите строку $this->middleware('auth');
как ты.
Затем скопируйте verify
метод из VerifiesEmails
черта к вашему VerificationController
и немного измени его. Метод должен выглядеть так:
public function verify(Request $request)
{
$user = User::find($request->route('id'));
if ($user->markEmailAsVerified())
event(new Verified($user));
return redirect($this->redirectPath())->with('verified', true);
}
Это переопределяет метод в VerifiesUsers
Черта и убирает проверку авторизации.
Безопасность (поправьте меня, если я ошибаюсь!)
Это все еще безопасно, поскольку запрос подписан и проверен. Кто-то может проверить адрес электронной почты другого пользователя, если он каким-то образом получит доступ к электронному письму с подтверждением, но в 99% случаев это вряд ли риск.
Вот более перспективное решение проблемы:
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
- Пользователь вводит учетные данные; успешно авторизован 2
- Пользователь будет перенаправлен обратно на URL-адрес подтверждения электронной почты
- Электронная почта будет отмечена как подтвержденная
1 На этом этапе электронное письмо не будет помечено как подтвержденное.
2 Пользователь может вводить неверные учетные данные несколько раз. Как только он введет правильные учетные данные, он будет перенаправлен на предполагаемый URL-адрес подтверждения электронной почты.
// For Laravel 6 and Above
use Illuminate\Auth\Events\Verified;
use Illuminate\Http\Request;
use App\User;
// comment auth middleware
//$this->middleware('auth');
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);
}
Решение, чтобы разрешить проверку электронной почты для пользователей, которые не вошли в систему (т.е. без авторизации):
Изменения в: app / Http / Controllers / Auth / VerificationController.php:
-
$this->middleware('auth');
к$this->middleware('auth')->except('verify');
- Скопируйте метод из
VerifiesEmails
черта. - Изменить метод проверки, чтобы он работал без ожидаемого
$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
Предложения
Если у вас есть предложения, как я могу улучшить этот код, дайте мне знать. Я был бы счастлив отредактировать этот ответ.
Вот мой взгляд на ситуацию. Проверка требует, чтобы пользователь вошел в систему, прежде чем он сможет завершить проверку, поэтому мы можем переопределить функцию проверки и войти в систему, используя идентификатор, который мы получили в ссылке. Это безопасная причина, по которой функция проверки не вызывается, если 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, поскольку он использует перенаправление для отслеживания ссылки.
Вы не должны удалять $this->middleware('auth')
в целом, поскольку это повлияет на перенаправления. Если вы удалите его, неаутентифицированные пользователи будут перенаправлены на "/email/verify" вместо "/login".
так $this->middleware('auth');
будет изменен на $this->middleware('auth')->except('verify');
в "VerificationController"
Также скопируйте функцию "verify" из "VerifyEmails" в "VerificationController".
добавьте эти две строки кода вверху функции
$user = User::find($request->route('id'));
auth()->login($user);
поэтому вы программно входите в систему, а затем выполняете дальнейшие действия
Я меняю EmailVerificationRequest, но теперь это неправильно, в любом случае это работает. Предупреждение . Это изменение поставщика
protected $user;
public function authorize()
{
$this->user = \App\Models\User::find($this->route('id'));
if ($this->user != null){
if (! hash_equals((string) $this->route('id'),
(string) $this->user->getKey())) {
return false;
}
if (! hash_equals((string) $this->route('hash'),
sha1($this->user->getEmailForVerification()))) {
return false;
}
return true;
}
return false;
}
Чтобы использовать внутреннюю логику laravel (без переопределения логики), мы просто создаем $request->user() и вызываем метод проверки типажа. И вручную войдите в систему пользователя, когда проверка будет успешной.
use VerifiesEmails {
verify as parentVerify;
}
public function verify(Request $request)
{
$user = User::find($request->route('id'));
if (!$user) return abort(404);
$request->setUserResolver(function () use($user) {
return $user;
});
return $this->parentVerify($request);
}
public function verified(Request $request)
{
Auth::login($request->user());
}
Если вы хотите активировать учетную запись пользователя без входа в систему, вы можете сделать это за 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);
}
Я бы, вероятно, занялся этим без использования встроенной в Laravel логики проверки электронной почты.
Вместо этого я бы следовал этим шагам.
- Создана таблица проверок с полем токена и адресом электронной почты.
- Когда пользователь регистрируется, создайте новую строку в таблице с уникальным токеном и его адресом электронной почты.
- Отправьте им письмо со ссылкой на сайт с токеном в качестве параметра запроса.
- Когда пользователь заходит на сайт, найдите подтверждение по токену и получите письмо
- Получите пользователя по электронной почте и отметьте как проверенное (возможно, какое-то поле в пользовательской таблице).
- Удалить подтверждение
- перенаправьте их туда, куда вы хотите, с сообщением сеанса о том, что они были проверены.
Просто предложение.