С использованием собственной аутентификации
Суть проекта такова: пользователь заполняет форму регистрации, после чего пользовательские данные заносятся в БД. Но сразу после этого пользователь не сможет зайти под своим именем на сайт, так как сначала он должен пройти проверку подлинности своего почтового ящика. Для осуществлении такой проверки, кроме стандартных полей в таблицу 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). Создадим:
- маршруты (post и get) для регистрации и авторизации;
- маршрут для проверки ключа, полученного пользователем в письме, отправленном ему после регистрации;
- маршрут для выхода (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