E-mail подтверждение регистрации пользователя в Laravel

Добавлено: 17/09/2016 06:47 |  Обновлено: 03/03/2019 14:22 |  Добавил: nick |  Просмотры: 12443 Комментарии: 22
Вводная часть
Сейчас уже довольно распространена процедура подтверждения введенного пользователем почтового ящика при регистрации, как это делается в Laravel описано в этом материале. Пример работает в Laravel 5.3-5.7.
В разделах ниже вы узнаете как добавить функционал подтверждения регистрации пользователя в свой проект. В первом разделе все создается вручную (функционал аутентификации + процедура подтверждения регистрации). Во втором разделе используется аутентификация из коробки (до версии 5.7). В третьем разделе используется аутентификация из коробки + функционал подтверждения регистрации, также из коробки (с версии 5.7).

Содержание:

  1. С использованием собственной аутентификации
  2. С использованием аутентификации из коробки
  3. E-mail подтверждение регистрации из коробки в Laravel 5.7

С использованием собственной аутентификации

Суть проекта такова: пользователь заполняет форму регистрации, после чего пользовательские данные заносятся в БД. Но сразу после этого пользователь не сможет зайти под своим именем на сайт, так как сначала он должен пройти проверку подлинности своего почтового ящика. Для осуществлении такой проверки, кроме стандартных полей в таблицу users (name, email, password) мы добавим 2 дполнительных поля: verified и token. Первое поле может принимать значения true/false (изначально false), второе поле служит для хранения случайного значения – ключа. На свой почтовый ящик пользователь получает сообщение со ссылкой, содержащей этот ключ из базы. После того как пользователь переходит по ссылке, ключ из ссылки и ключ из базы сравниваются, при их совпадении поле verified переходит в состояние true, а ключ удаляется. Активация считается завершенной. После этого пользователь сможет успешно пройти аутентификацию. Для работы с БД нам потребуется изменить стандартный файл миграции 2014_10_12_000000_create_users_table.php, добавим в него необходимые поля: verified и token. После чего содержимое метода up() будет таким:
Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password', 60);
    $table->boolean('verified')->default(false);
    $table->string('token')->nullable();
    $table->rememberToken();
    $table->timestamps();
});
Перейдем в файл маршрутов (web.php). Создадим:
  1. маршруты (post и get) для регистрации и авторизации;
  2. маршрут для проверки ключа, полученного пользователем в письме, отправленном ему после регистрации;
  3. маршрут для выхода (logout) со страницы администратора (dashboard) и для входа на нее.
Готовый файл будет таким:
Route::get('/', function () {
    return view('welcome');
});

Route::get('register', 'RegistrationController@register');
Route::post('register', 'RegistrationController@postRegister');

Route::get('register/confirm/{token}', 'RegistrationController@confirmEmail');

Route::get('login', 'SessionsController@login')->middleware('guest');
Route::post('login', 'SessionsController@postLogin')->middleware('guest');
Route::get('logout', 'SessionsController@logout');

Route::get('dashboard', ['middleware' => 'auth', function() {
    return 'Добро пожаловать, '.Auth::user()->name.'!';
}]); 
Далее создадим первый контроллер RegistrationController. В него добавим метод confirmEmail(), который подтверждает введенный пользователем почтовый адрес:
public function confirmEmail(Request $request, $token)
{
    User::whereToken($token)->firstOrFail()->confirmEmail();

    $request->session()->flash('message', 'Учетная запись подтверждена. Войдите под своим именем.');

    return redirect('login');
}
Как вы могли заметить по файлу маршрутов, этот контроллер содержит еще два метода: register() и postRegister(). Первый отвечает за генерацию формы регистрации:
public function register()
{
    return view('auth.register');
}
Второй за проверку данных из этой формы, заполненной пользователем:
public function postRegister(Request $request)
{
    $this->validate($request, [
        'name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required'
    ]);

    $user = User::create($request->all());

    Mail::to($user)->send(new UserRegistered($user));

    $request->session()->flash('message', 'На ваш адрес было выслано письмо с подтверждением регистрации.');

    return redirect()->back();
}
В конструкторе контроллера вызовем middleware:
public function __construct()
{
    $this->middleware('guest');
}
Также не забудьте в заголовке контроллера добавить нужные строчки для классов UserRegistered и Mail. В результате чего заголовок должен выглядеть следующим образом:
namespace App\Http\Controllers;

use App\User;
use App\Mail\UserRegistered;
use Illuminate\Support\Facades\Mail;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
Второй из нужных нам контроллеров, это SessionsController. В нем создадим 5 методов. Первый метод – это метод с формой для входа:
public function login()
{
    return view('auth.login');
}
Второй для обработки данных с этой формы:
public function postLogin(Request $request)
{
    $this->validate($request, ['email' => 'required|email', 'password' => 'required']);

    if ($this->signIn($request)) {
        $request->session()->flash('message', 'Добро пожаловать!');

        return redirect()->intended('dashboard');
    }

    $request->session()->flash('message', 'Вход запрещен!');

    return redirect()->back();
}
Третий для выхода (logout):
public function logout(Request $request)
{
    Auth::logout();

    $request->session()->flash('message', 'Вы разлогинились.');

    return redirect('login');
}
Четвертый и пятый для проверки данных из формы (используются во втором методе):
protected function signIn(Request $request)
{
    return Auth::attempt($this->getCredentials($request), $request->has('remember'));
}
protected function getCredentials(Request $request)
{
    return [
        'email'    => $request->input('email'),
        'password' => $request->input('password'),
        'verified' => true
    ];
}
В первом контроллере RegistrationController есть строчка:
Mail::to($user)->send(new UserRegistered($user));
С помощью нее мы отправляем письмо пользователю после заполнения формы регистрации. Начиная с версии 5.3 для отправки писем можно воспользоваться классом Mailable. Подробнее об отправке писем можно почитать в документации, в разделе Mail. Чтобы создать свой mailable-класс (в нашем случае класс с именем UserRegistered) нужно выполнить команду:
php artisan make:mail UserRegistered
После чего в директории проекта появится новая папка Mail, а в ней файл UserRegistered.php, с mailable-классом. Для нашего проекта содержимое класса UserRegistered будет следующим:
use Queueable, SerializesModels;

public $user;

public function __construct(User $user)
{
    $this->user = $user;
}

public function build()
{
    return $this->from('admin@site.com')
            ->view('emails.confirm');
}
В методе build() мы используем вид confirm.blade.php, в котором содержится структура письма. Структура может быть такая:
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Активация регистрации нового ползователя</title>
</head>
<body>
    <h1>Спасибо за регистрацию, {{$user->name}}!</h1>

    <p>
        Перейдите <a href='{{ url("register/confirm/{$user->token}") }}'>по ссылке </a>чтобы завершить регистрацию!
    </p>
</body>
</html>
На этапе разработки для получения писем можно воспользоваться файлом laravel.log. Для этого в файле .env в строчке MAIL_DRIVER нужно указать log.
Переходим к модели User. В модели нам потребуется три метода. Первый – это boot()-метод, который выполняется при загрузке модели. С помощью него мы создаем ключ подтверждения, отправляемый пользователю. Необходимый метод будет выглядеть следующим образом:
public static function boot()
{
    parent::boot();

    static::creating(function ($user) {
        $user->token = str_random(30);
    });
}
Во втором методе мы хэшируем пароль пользователя:
public function setPasswordAttribute($password)
{
    $this->attributes['password'] = bcrypt($password);
}
В третьем методе, при успешной валидации пользователя мы удаляем ключ из базы и задаем статус поля verified равным true:
public function confirmEmail()
{
    $this->verified = true;
    $this->token = null;

    $this->save();
}
Осталось создать виды. Напоминаю, что для письма вид уже был создан. Основной вид приложения (используется Bootstrap):
<html>
<head>
    <meta charset="UTF-8">
    <title>E-mail активация</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        @include('flash')

        @yield('content')
    </div>
</body>
</html>
В этом виде используется вложенный вид flash для вывода сообщений:
@if (session()->has('message'))
    <div class="alert alert-info">{{ session('message') }}</div>
@endif
Вид с формой для регистрации:
@extends('app')

@section('content')
<h1>Регистрация</h1>

@include('errors')

<form method="POST" action='{{ url("register/") }}'>
    {!! csrf_field() !!}

    <div class="form-group">
        <label for="name">Имя:</label>
        <input type="text" name="name" id="name" class="form-control" value="{{ old('name') }}">
    </div>

    <div class="form-group">
        <label for="email">Email:</label>
        <input type="text" name="email" id="email" class="form-control" value="{{ old('email') }}">
    </div>

    <div class="form-group">
        <label for="password">Пароль:</label>
        <input type="password" name="password" id="password" class="form-control">
    </div>

    <div class="form-group">
        <button type="submit" class="btn btn-default">Зарегистрироваться</button>
    </div>
    </form>
@stop
Вид с формой для входа на сайт:
@extends('app')

@section('content')
    <h1>Авторизация</h1>

    @include('errors')

    <form method="POST" action='{{ url("login/") }}'>
        {{csrf_field()}}

        <div class="form-group">
            <label for="email">Email:</label>
            <input type="text" name="email" id="email" class="form-control" value="{{ old('email') }}">
        </div>

        <div class="form-group">
            <label for="password">Пароль:</label>
            <input type="password" name="password" id="password" class="form-control">
        </div>

        <div class="checkbox">
            <label>
                <input type="checkbox" name="remember" id="remember" {{ old('remember') ? ' checked' : '' }}> Запомнить меня
            </label>
        </div>

        <div class="form-group">
            <button type="submit" class="btn btn-default">Войти</button>
        </div>
    </form>
@stop
Оба вида (регистрации и авторизации) используют вложенный вид errors:
@if (count($errors) > 0)
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
К содержанию

Оставьте свой комментарий

Комментарии

  • Максим  02/03/2019 15:39

    Делал для лары 5.7

    nick     Максим
    В ларе 5.7 E-mail подтверждение регистрации пользователя работает "из коробки". Добавил материал на сайт.
    Ссылка
    • Ответить
  • Максим  02/03/2019 15:38

    Спасибо. Чуть переделал, но все норм.

    Ссылка
    • Ответить
  • Саша  ( http://posmotrim.by 24/11/2018 22:05

    А про добавление отправки писем в очередь очень хотелось бы почитать. Как то очень мало информации про это.

    Ссылка
    • Ответить
  • Антон  22/09/2018 08:04

    В алгоритме есть "дырка". Подтверждение регистрации происходит тогда, когда появляется окно входа при переходе по ссылке из письма (понаблюдайте за базой в процессе регистрации). Пароль и логин вводятся, когда учетная запись в базе уже активирована. В общем можно набрать любой существующий почтовый ящик (даже по ошибке), чужой человек из любопытства перейдет по ссылке из письма и активирует чужую учетку без паролей и логинов. Ну дальше сменть пароль не составляет труда и т.д. Не копируйте бездумно чужие решения!!!! А автору бы исправить этот недочет))))

    Ссылка
    • Ответить
  • Антон  ( https://vk.com/gimtonic 24/06/2018 14:57

    Все у кого ошибка с $request->filled используйте $request->has!!!

    Ссылка
    • Ответить
  • Daniil  23/04/2018 14:10

    не ставлю обычно , а это может влиять на работу ? 

    nick     Daniil
    Ругается то на этот метод $request->filled('remember'). Возможно какой-то конфликт имен, раз фреймворк свой собственный метод найти не может.
    Ссылка
    • Ответить
  • Daniil  23/04/2018 13:48

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

    nick     Daniil
    Вообще то метод filled() относится к самому фреймворку. if ($request->filled('name')) { // } А вы галочку Remember Me ставите, когда заходите в кабиент?
    Ссылка
    • Ответить
  • Daniil  ( https://webtrade.solutions/ 23/04/2018 13:20

    (1/1) BadMethodCallException

    Method filled does not exist.

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

    nick     Daniil
    Не знаю, что за метод filled. у меня нет такого :)
    Ссылка
    • Ответить
  • Daniil  ( https://webtrade.solutions 19/04/2018 18:22

    (1/1) FatalThrowableError

    Type error: Argument 1 passed to App\Mail\UserRegistered::__construct() must be an instance of App\Mail\User, instance of App\User given, called in /home/w/webtrade/webtrade.solutions/app/Http/Controllers/Auth/RegisterController.php on line 90

    не совсем понимаю в чем ошибка . подскажите пожалуйста

    nick     Daniil
    В файл UserRegistered.php нужно добавить use App\User.
    Ссылка
    • Ответить
  • Rooter  10/03/2018 02:55

    Здравствуйте. Не пойму, про какую инструкцию все тут пишут в комментах? Я вижу статью из одного абзаца... тут только общая логика по-моему (за нее спасибо), но не инструкйия. У меня такое ощущение, что я не ту статью вижу, что все видят. :(

    nick     Rooter
    Спасибо за комментарий! Сейчас должны видеть то, что видели все.
    Ссылка
    • Ответить
  • Алик  06/02/2018 15:58

    Ок. Cпасибо.

    nick     Алик
    Не за что. Пожалуй добавлю это в материал.
    Ссылка
    • Ответить
  • Алик  06/02/2018 13:03

    Но  я это делал ПОЛНОСТЬЮ СОГЛАСНО Вашей инструкции и файл UserRegistered в папке app/Mail у меня появился. И ругаелся после всего этого.

    nick     Алик
    Значит не добавили строчку: use App\Mail\UserRegistered; вначале файла (перед названием класса).
    Ссылка
    • Ответить
  • Алик  06/02/2018 12:04

    Mail::to($user)->send(new UserRegistered($user));--- ругается на эту строку, что нет такого UserRegistered, но когда сделал вот так в самом RegisterController.php(который из кокробки)

    Mail::send('emails.welcome',['token'=>$token],function($u) use ($user)

    {

    $u->from('alikamatov@site.ru');

    $u->to($user->email);

    $u->subject('Привет');

    });

    почему то проехало . Но мне Ваш вариант более удобен. Подскажите пожалуйста где у меня может быть ошибка

    nick     Алик
    Ругается потому что класса такого нет. Создайте его следующей командой: php artisan make:mail UserRegistered После чего он должен быть в папке app/Mail.
    Ссылка
    • Ответить
  • Алик  27/01/2018 08:12

    Большое спасибо. 

    Ссылка
    • Ответить
  • Алик  26/01/2018 19:13

    Я  извиняюсь за назойливость и незнание , но если Вам нетрудно пояните пожалуйста "admin@site.com" это почта которую выдает Почтовый сервис или это еще, что то в чем еше надо разобираться.

    nick     Алик
    Да, вы правы, данный адрес выдается почтовым сервисом, который отвечает за пересылку писем. В данном методе: public function build() { return $this->from('admin@site.com') ->view('emails.confirm'); } admin@site.com можно в принципе вообще не указывать, адрес отправки может быть указан в файле .env или в файле config/mail.php.
    Ссылка
    • Ответить
  • Алик  24/01/2018 11:28

    Большое спасибо за ответ! Попытаюсь .

    Ссылка
    • Ответить
  • Алик  23/01/2018 20:09

    Подскажите пожайлуста. У меня ларавель 5.3. Сайт делаю на Openserver-е. Аутентификация из коробки - и работает отлично. Нужно ли  чтобы сделать верификацию воспользовавшись Вашим кодом грохать Аутентификацию (которая из коробки). Заранее  спасибо.

    nick     Алик
    На мой взгляд идеальный вариант - встроить в аутентификацию из коробки код e-mail-подтверждения регистрации пользователя. На сайте есть материал - Кастомизация встроенной в Laravel формы регистрации. Он должен помочь.
    Ссылка
    • Ответить
  • Алик  22/01/2018 17:40

    1. "Переходим к модели. В модели нам потребуется три метода." -- А что за модель и где ее искать?

    2." return $this->from('admin@site.com') ->view('emails.confirm');"---А это что 'admin@site.com?

    Все остальное понятно но без этого стоппора

    nick     Алик
    Модель - User. Она есть по-умолчанию в каждом проекте. Спасибо за замечание. admin@site.com - это адрес сайта, на котором пользователь регистрируется. Пользователь получает письмо с подтверждением регистрации на свою почту именно с этого адреса.
    Ссылка
    • Ответить
  • Jaan  07/02/2017 10:16

    Спасибо сделал вот так 

    public function build()
        {
            return $this->view('emails.verification')->subject('Подтверждение регистрации');
        }

    Ссылка
    • Ответить
  • Jaan  07/02/2017 07:17

    Николай не подскажете как изменить заголовок отправляемого письма вот тут скрин. Где это дело изменить? 

    nick     Jaan
    Вы можете задать параметры для отправляемого письма в методе build() вашего mailable-класса. Например: public function build() { return $this->from($address, $name) ->subject($subject) ->view('emails.confirm'); }
    Ссылка
    • Ответить
  • Jaan  06/02/2017 13:07

    Странно выглядит. Это для какой версии laravel ?

    nick     Jaan
    Для пятой, я тестировал код в версии 5.3.
    Ссылка
    • Ответить
  • kdes70  24/11/2016 22:28

    Спасибо!

    Ссылка
    • Ответить