Полный цикл в digital

Аутентификация в Laravel

Аутентификация это процесс регистрации и логина пользователей. Не путать с авторизацией проверкой прав уже залогиненного пользователя. В Laravel сделать аутентификацию очень просто — почти всё готово из коробки. Конфигурационный файл аутентификации расположен в config/auth.php, он содержит несколько опций для тонкой настройки служб аутентификации. По сути средства аутентификации Laravel состоят из гардов и провайдеров:

  • Гарды определяют как именно аутентифицируются пользователи, для каждого запроса. Laravel поставляется с гардом session, который поддерживает состояние аутентифицированности с помощью хранилища сессий и кук
  • Провайдеры определяют как именно пользователи извлекаются из базы данных. Laravel поставляется с поддержкой извлечения пользователей с помощью Eloquent и конструктора запросов БД. При необходимости, можно определить дополнительные провайдеры:
config/auth.php<?php
return [
    /*
    |--------------------------------------------------------------------------
    | Проверка подлинности по умолчанию
    |--------------------------------------------------------------------------
    |
    | Этот параметр управляет аутентификацией по умолчанию "guard" и паролем
    | сбросьте параметры для вашего приложения. Вы можете изменить эти значения по умолчанию
    | по мере необходимости, но они являются идеальным началом для большинства приложений.
    |
    */
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    /*
    |--------------------------------------------------------------------------
    | Средства защиты от аутентификации
    |--------------------------------------------------------------------------
    |
    | Далее вы можете определить все средства аутентификации для своего приложения.
    | Конечно, для вас была определена отличная конфигурация по умолчанию
    | здесь, который использует хранилище сеансов и Eloquentuserprovider.
    |
    | У всех драйверов аутентификации есть поставщик пользователя. Это определяет, как
    | пользователи фактически извлекаются из вашей базы данных или другого хранилища
    | механизмы, используемые этим приложением для сохранения данных вашего пользователя.
    |
    | Поддерживаемый: "session"
    |
    */
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    ],
    /*
    |--------------------------------------------------------------------------
    | Поставщики услуг для пользователей
    |--------------------------------------------------------------------------
    |
    | У всех драйверов аутентификации есть поставщик пользователя. Это определяет, как
    | пользователи фактически извлекаются из вашей базы данных или другого хранилища
    | механизмы, используемые этим приложением для сохранения данных вашего пользователя.
    |
    | Если у вас есть несколько пользовательских таблиц или моделей, вы можете настроить несколько
    | источники, которые представляют каждую модель / таблицу. Эти источники могут затем
    | быть назначены любым дополнительным средствам аутентификации, которые вы определили.
    |
    | Поддерживаемый: "database", "eloquent"
    |
    */
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
    /*
    |--------------------------------------------------------------------------
    | Сброс паролей
    |--------------------------------------------------------------------------
    |
    | Вы можете указать несколько конфигураций сброса пароля, если у вас есть больше
    | более одной пользовательской таблицы или модели в приложении, и вы хотите иметь
    | отдельные настройки сброса пароля в зависимости от конкретных типов пользователей.
    |
    | Время истечения срока действия - это количество минут, в течение которых каждый токен сброса будет
    | считается действительным. Эта функция безопасности сохраняет токены недолговечными, поэтому
    | у них меньше времени на то, чтобы быть угаданными. Вы можете изменить это по мере необходимости.
    |
    */
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],
    /*
    |--------------------------------------------------------------------------
    | Тайм-аут подтверждения пароля
    |--------------------------------------------------------------------------
    |
    | Здесь вы можете определить количество секунд до подтверждения пароля
    | время ожидания истекает, и пользователю предлагается повторно ввести свой пароль через
    | экран подтверждения. По умолчанию тайм-аут длится три часа.
    |
    */
    'password_timeout' => 10800,
];

Session

Из коробки аутентификация в Laravel строится на сессиях, файлы с кешем ссесий храняться в директории storage/framework/sessions.

Создание заготовок

Для создания заготовок всех необходимых для аутентификации контроллеров, шаблонов и роутов предназначен пакет laravel/ui:

composer require laravel/ui --dev
php artisan ui:auth
npm install && npm run dev

Будут созданы контроллеры:

  • RegisterController обеспечивает регистрацию пользователей
  • LoginController обеспечивает аутентификацию пользователей
  • ForgotPasswordController отправляет письмо на сброс пароля
  • ResetPasswordController содержит логику для сброса паролей

Будут созданы шаблоны:

  • auth.register форма регистрации пользователей
  • auth.login форма аутентификации пользователей
  • auth.passwords.email форма для ввода адреса почты (восстановление пароля)
  • auth.passwords.reset форма для ввода нового пароля (восстановление пароля)

Будут добавлены маршруты, вызов Auth::routes() добавляет сразу около десятка маршрутов, посмотреть эти маршруты можно в классе Laravel\Ui\AuthRouteMethods:

routes/web.phpAuth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
vendor/laravel/ui/src/AuthRouteMethods.php<?php
namespace Laravel\Ui;
class AuthRouteMethods
{
    /**
        * Register the typical authentication routes for an application.
        *
        * @param  array  $options
        * @return callable
        */
    public function auth()
    {
        return function ($options = []) {
            $namespace = class_exists($this->prependGroupNamespace('Auth\LoginController')) ? null : 'App\Http\Controllers';
            $this->group(['namespace' => $namespace], function() use($options) {
                // Login Routes...
                if ($options['login'] ?? true) {
                    $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
                    $this->post('login', 'Auth\LoginController@login');
                }
                // Logout Routes...
                if ($options['logout'] ?? true) {
                    $this->post('logout', 'Auth\LoginController@logout')->name('logout');
                }
                // Registration Routes...
                if ($options['register'] ?? true) {
                    $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
                    $this->post('register', 'Auth\RegisterController@register');
                }
                // Password Reset Routes...
                if ($options['reset'] ?? true) {
                    $this->resetPassword();
                }
                // Password Confirmation Routes...
                if ($options['confirm'] ??
                    class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) {
                    $this->confirmPassword();
                }
                // Email Verification Routes...
                if ($options['verify'] ?? false) {
                    $this->emailVerification();
                }
            });
        };
    }
    /**
        * Register the typical reset password routes for an application.
        *
        * @return callable
        */
    public function resetPassword()
    {
        return function () {
            $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
            $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
            $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
            $this->post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
        };
    }
    /**
        * Register the typical confirm password routes for an application.
        *
        * @return callable
        */
    public function confirmPassword()
    {
        return function () {
            $this->get('password/confirm', 'Auth\ConfirmPasswordController@showConfirmForm')->name('password.confirm');
            $this->post('password/confirm', 'Auth\ConfirmPasswordController@confirm');
        };
    }
    /**
        * Register the typical email verification routes for an application.
        *
        * @return callable
        */
    public function emailVerification()
    {
        return function () {
            $this->get('email/verify', 'Auth\VerificationController@show')->name('verification.notice');
            $this->get('email/verify/{id}/{hash}', 'Auth\VerificationController@verify')->name('verification.verify');
            $this->post('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');
        };
    }
}

Контроллеры

RegisterController

Контроллер RegisterController в сочетании с трейтом RegistersUsers показывает форму регистрации, проводит валидацию данных и создает нового пользователя и выполняет редирект на страницу название_сайта.com/home. Свойство $redirectTo определяет, куда будет перенаправлен пользователь после регистрации. Метод validator() определяет способ валидации регистрационных данных. А метод create() — как создать нового пользователя на основе полученных регистрационных данных:

namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller {
    use RegistersUsers;
    protected $redirectTo = RouteServiceProvider::HOME;
    public function __construct() {
        $this->middleware('guest');
    }
    protected function validator(array $data) {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
    protected function create(array $data) {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
}
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
trait RegistersUsers {
    use RedirectsUsers;
    public function showRegistrationForm() {
        return view('auth.register');
    }
    public function register(Request $request) {
        $this->validator($request->all())->validate();
        event(new Registered($user = $this->create($request->all())));
        $this->guard()->login($user);
        if ($response = $this->registered($request, $user)) {
            return $response;
        }
        return $request->wantsJson()
                    ? new JsonResponse([], 201)
                    : redirect($this->redirectPath());
    }
    protected function guard() {
        return Auth::guard();
    }
    protected function registered(Request $request, $user) {
        // .....
    }
}

LoginController

Контроллер LoginController позволяет пользователю выполнить вход в систему. Он подгружает трейт AuthenticatesUsers, подгружает RedirectUsers и ThrottlesLogins который запрещает использовать форму входа пользователям с большим количеством неудачных попыток входа.. Свойство $redirectTo определяет, куда будет перенаправлен пользователь после аутентификации:

namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller {
    use AuthenticatesUsers;
    protected $redirectTo = RouteServiceProvider::HOME;
    public function __construct() {
        $this->middleware('guest')->except('logout');
    }
}
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
trait AuthenticatesUsers {
    use RedirectsUsers, ThrottlesLogins;
    public function showLoginForm() {
        return view('auth.login');
    }
    public function login(Request $request) {
        $this->validateLogin($request);
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
            return $this->sendLockoutResponse($request);
        }
        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }
        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);
        return $this->sendFailedLoginResponse($request);
    }
    protected function validateLogin(Request $request) {
        $request->validate([
            $this->username() => 'required|string',
            'password' => 'required|string',
        ]);
    }
    protected function attemptLogin(Request $request) {
        return $this->guard()->attempt(
            $this->credentials($request), $request->filled('remember')
        );
    }
    protected function credentials(Request $request) {
        return $request->only($this->username(), 'password');
    }
    protected function sendLoginResponse(Request $request) {
        $request->session()->regenerate();
        $this->clearLoginAttempts($request);
        if ($response = $this->authenticated($request, $this->guard()->user())) {
            return $response;
        }
        return $request->wantsJson()
                    ? new JsonResponse([], 204)
                    : redirect()->intended($this->redirectPath());
    }
    protected function authenticated(Request $request, $user) {
        // .....
    }
    protected function sendFailedLoginResponse(Request $request) {
        throw ValidationException::withMessages([
            $this->username() => [trans('auth.failed')],
        ]);
    }
    public function username() {
        return 'email';
    }
    public function logout(Request $request) {
        $this->guard()->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();
        if ($response = $this->loggedOut($request)) {
            return $response;
        }
        return $request->wantsJson()
            ? new JsonResponse([], 204)
            : redirect('/');
    }
    protected function loggedOut(Request $request) {
        // .....
    }
    protected function guard() {
        return Auth::guard();
    }
}

ResetPasswordController

Контроллер ResetPasswordController подгружает трейт ResetsPasswords. Этот трейт показывает форму сброса пароля, метод showResetForm(), обрабатывает POST-запрос, выполянет валидацию и сбрасывает пароль:

namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller {
    use ResetsPasswords;
    protected $redirectTo = RouteServiceProvider::HOME;
}
namespace Illuminate\Foundation\Auth;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
trait ResetsPasswords {
    use RedirectsUsers;
    public function showResetForm(Request $request, $token = null) {
        return view('auth.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }
    public function reset(Request $request) {
        $request->validate($this->rules(), $this->validationErrorMessages());
        // Here we will attempt to reset the user's password. If it is successful we
        // will update the password on an actual user model and persist it to the
        // database. Otherwise we will parse the error and return the response.
        $response = $this->broker()->reset(
            $this->credentials($request), function ($user, $password) {
                $this->resetPassword($user, $password);
            }
        );
        // If the password was successfully reset, we will redirect the user back to
        // the application's home authenticated view. If there is an error we can
        // redirect them back to where they came from with their error message.
        return $response == Password::PASSWORD_RESET
                    ? $this->sendResetResponse($request, $response)
                    : $this->sendResetFailedResponse($request, $response);
    }
    protected function rules() {
        return [
            'token' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed|min:8',
        ];
    }
    protected function validationErrorMessages() {
        return [];
    }
    protected function credentials(Request $request) {
        return $request->only(
            'email', 'password', 'password_confirmation', 'token'
        );
    }
    protected function resetPassword($user, $password) {
        $this->setUserPassword($user, $password);
        $user->setRememberToken(Str::random(60));
        $user->save();
        event(new PasswordReset($user));
        $this->guard()->login($user);
    }
    protected function setUserPassword($user, $password) {
        $user->password = Hash::make($password);
    }
    protected function sendResetResponse(Request $request, $response) {
        if ($request->wantsJson()) {
            return new JsonResponse(['message' => trans($response)], 200);
        }
        return redirect($this->redirectPath())
                            ->with('status', trans($response));
    }
    protected function sendResetFailedResponse(Request $request, $response) {
        if ($request->wantsJson()) {
            throw ValidationException::withMessages([
                'email' => [trans($response)],
            ]);
        }
        return redirect()->back()
                    ->withInput($request->only('email'))
                    ->withErrors(['email' => trans($response)]);
    }
    public function broker() {
        return Password::broker();
    }
    protected function guard() {
        return Auth::guard();
    }
}

ForgotPasswordController

Контроллер ForgotPasswordController подгружает трейт SendsPasswordResetEmails. Он показывает форму с помощью метода showLinkRequestForm, обрабатывает POST-запрос с помощию метода sendResetLinkEmail:

namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller {
    use SendsPasswordResetEmails;
}
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;
trait SendsPasswordResetEmails {
    public function showLinkRequestForm() {
        return view('auth.passwords.email');
    }
    public function sendResetLinkEmail(Request $request) {
        $this->validateEmail($request);
        // We will send the password reset link to this user. Once we have attempted
        // to send the link, we will examine the response then see the message we
        // need to show to the user. Finally, we'll send out a proper response.
        $response = $this->broker()->sendResetLink(
            $this->credentials($request)
        );
        return $response == Password::RESET_LINK_SENT
                    ? $this->sendResetLinkResponse($request, $response)
                    : $this->sendResetLinkFailedResponse($request, $response);
    }
    protected function validateEmail(Request $request) {
        $request->validate(['email' => 'required|email']);
    }
    protected function credentials(Request $request) {
        return $request->only('email');
    }
    protected function sendResetLinkResponse(Request $request, $response) {
        return $request->wantsJson()
                    ? new JsonResponse(['message' => trans($response)], 200)
                    : back()->with('status', trans($response));
    }
    protected function sendResetLinkFailedResponse(Request $request, $response) {
        if ($request->wantsJson()) {
            throw ValidationException::withMessages([
                'email' => [trans($response)],
            ]);
        }
        return back()
                ->withInput($request->only('email'))
                ->withErrors(['email' => trans($response)]);
    }
    public function broker() {
        return Password::broker();
    }
}

HomeController

Контроллер HomeController — после ввода логина и пароля пользователь будет отправлен на эту страницу:

namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller {
    public function __construct() {
        $this->middleware('auth');
    }
    public function index() {
        return view('home');
    }
}

Шаблоны

Шаблон формы для регистрации resources/views/auth/register.blade.php:

resources/views/auth/register.blade.php@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Register') }}</div>
                <div class="card-body">
                    <form method="POST" action="{{ route('register') }}">
                        @csrf
                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror"
                                        name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">
                                {{ __('E-Mail Address') }}
                            </label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror"
                                        name="email" value="{{ old('email') }}" required autocomplete="email">
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">
                                {{ __('Password') }}
                            </label>
                            <div class="col-md-6">
                                <input id="password" type="password" name="password" autocomplete="new-password"
                                        required class="form-control @error('password') is-invalid @enderror">
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">
                                {{ __('Confirm Password') }}
                            </label>
                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control"
                                        name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Register') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Шаблон формы для аутентификации (ввод логина и пароля) resources/views/auth/login.blade.php:

resources/views/auth/login.blade.php@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>
                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">
                                {{ __('E-Mail Address') }}
                            </label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror"
                                        name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">
                                {{ __('Password') }}
                            </label>
                            <div class="col-md-6">
                                <input id="password" type="password" name="password" autocomplete="current-password"
                                        required class="form-control @error('password') is-invalid @enderror">
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember"
                                            id="remember" {{ old('remember') ? 'checked' : '' }}>
                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>
                                @if (Route::has('password.request'))
                                    <a class="btn btn-link" href="{{ route('password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Шаблон формы для ввода адреса почты при сбросе пароля resources/views/auth/password/reset.blade.php:

resources/views/auth/password/reset.blade.php@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Reset Password') }}</div>
                <div class="card-body">
                    <form method="POST" action="{{ route('password.update') }}">
                        @csrf
                        <input type="hidden" name="token" value="{{ $token }}">
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">
                                {{ __('E-Mail Address') }}
                            </label>
                            <div class="col-md-6">
                                <input id="email" type="email" name="email" value="{{ $email ?? old('email') }}"
                                        class="form-control @error('email') is-invalid @enderror"
                                        required autocomplete="email" autofocus>
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">
                                {{ __('Password') }}
                            </label>
                            <div class="col-md-6">
                                <input id="password" type="password" name="password" autocomplete="new-password"
                                        required class="form-control @error('password') is-invalid @enderror">
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">
                                {{ __('Confirm Password') }}
                            </label>
                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control"
                                        name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Reset Password') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Шаблон формы для ввода нового пароля при сбросе пароля resources/views/auth/password/email.blade.php:

resources/views/auth/password/email.blade.php@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Reset Password') }}</div>
                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif
                    <form method="POST" action="{{ route('password.email') }}">
                        @csrf
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">
                                {{ __('E-Mail Address') }}
                            </label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror"
                                        name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Send Password Reset Link') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Проверка

Теперь пользователи могут регистрироваться и аутентифицироваться:

  • http://название_сайта.com/register форма регистрации нового пользователя
  • http://название_сайта.com/home перенаправление после успешной аутентификации пользователя
  • http://название_сайта.com/login вход в систему
  • http://название_сайта.com/password/reset форма для восстановления пароля

Перевод на русский язык

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

composer require laravel-lang/lang:~9.0

В config/app.php изменить locale:

'locale' => 'ru'

Создаем в папке resources путь lang и переносим директорию vendor/laravel-lang/lang/locales/ru.

Должно получится resources/lang/ru, из этой дириктории переносим файл ru.json в директорию resources/lang.

resources/lang/ru/auth.phpreturn [
    /*
    |--------------------------------------------------------------------------
    | Языковые ресурсы аутентификации
    |--------------------------------------------------------------------------
    |
    | Следующие языковые ресурсы используются во время аутентификации для
    | различных сообщений которые мы должны вывести пользователю на экран.
    | Вы можете свободно изменять эти языковые ресурсы в соответствии
    | с требованиями вашего приложения.
    |
    */
    'failed'   => 'Неверное имя пользователя или пароль.',
    'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте еще раз через :seconds секунд.',
];
resources/lang/ru/passwords.phpreturn [
    /*
    |--------------------------------------------------------------------------
    | Языковые ресурсы напоминания пароля
    |--------------------------------------------------------------------------
    |
    | Последующие языковые строки возвращаются брокером паролей на неудачные
    | попытки обновления пароля в таких случаях, как ошибочный код сброса
    | пароля или неверный новый пароль.
    |
    */
    'reset'     => 'Ваш пароль был сброшен!',
    'sent'      => 'Ссылка на сброс пароля была отправлена!',
    'throttled' => 'Пожалуйста, подождите перед повторной попыткой.',
    'token'     => 'Ошибочный код сброса пароля.',
    'user'      => 'Не удалось найти пользователя с указанным электронным адресом.',
];
resources/lang/ru.json{
    "A fresh verification link has been sent to your email address.": "Новая ссылка для подтверждения была отправлена на Ваш email-адрес.",
    "All rights reserved.": "Все права защищены.",
    "Before proceeding, please check your email for a verification link.": "Прежде чем продолжить, пожалуйста, проверьте ссылку подтверждения в своей электронной почте.",
    "click here to request another": "нажмите здесь, чтобы запросить еще раз",
    "Confirm Password": "Подтверждение пароля",
    "Dashboard": "Главная",
    "E-Mail Address": "Адрес почты",
    "Error": "Ошибка",
    "Forbidden": "Запрещено",
    "Forgot Your Password?": "Забыли пароль?",
    "Go Home": "Домой",
    "Hello!": "Здравствуйте!",
    "hi": "привет",
    "If you did not create an account, no further action is required.": "Если Вы не создавали учетную запись, никаких дополнительных действий не требуется.",
    "If you did not receive the email": "Если Вы не получили письмо",
    "If you did not request a password reset, no further action is required.": "Если Вы не запрашивали сброс пароля, то дополнительных действий не требуется.",
    "If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Если у Вас возникли проблемы с кликом по кнопке \":actionText\", скопируйте и вставьте адрес\nв адресную строку браузера:",
    "Invalid signature.": "Неверная подпись.",
    "Login": "Войти",
    "Logout": "Выйти",
    "Name": "Имя",
    "Not Found": "Не найдено",
    "Oh no": "О, нет",
    "Page Expired": "Страница устарела",
    "Page Not Found": "Страница не найдена",
    "Password": "Пароль",
    "Please click the button below to verify your email address.": "Пожалуйста, нажмите кнопку ниже, чтобы подтвердить свой адрес электронной почты.",
    "Please confirm your password before continuing.": "Пожалуйста, подтвердите Ваш пароль перед продолжением.",
    "Regards": "С уважением",
    "Register": "Регистрация",
    "Remember Me": "Запомнить меня",
    "Reset Password": "Сбросить пароль",
    "Reset Password Notification": "Уведомление сброса пароля",
    "Send Password Reset Link": "Отправить ссылку для сброса пароля",
    "Server Error": "Ошибка сервера",
    "Service Unavailable": "Сервис недоступен",
    "This action is unauthorized.": "Неавторизованное действие.",
    "Sorry, the page you are looking for could not be found.": "Извините, страница, которую вы ищете, не найдена.",
    "Sorry, you are forbidden from accessing this page.": "Извините, вам запрещено пользоваться этой страницей.",
    "Sorry, you are making too many requests to our servers.": "Извините, вы делаете слишком много запросов на наши серверы.",
    "Sorry, you are not authorized to access this page.": "К сожалению, у вас нет доступа к этой странице.",
    "Sorry, your session has expired. Please refresh and try again.": "К сожалению, срок действия вашей сессии истек. Обновите и повторите попытку.",
    "Sorry, we are doing some maintenance. Please check back soon.": "Извините, мы проводим некоторые работы. Пожалуйста, зайдите позже.",
    "This password reset link will expire in :count minutes.": "Срок действия ссылки для сброса пароля истекает через :count минут.",
    "Toggle navigation": "Переключить навигацию",
    "Too Many Attempts.": "Слишком много попыток.",
    "Too Many Requests": "Слишком много запросов",
    "Unauthorized": "Не авторизован",
    "Verify Email Address": "Подтвердить email-адрес",
    "Verify Your Email Address": "Подтвердите Ваш email-адрес",
    "We won't ask for your password again for a few hours.": "Мы не будем запрашивать Ваш пароль вновь в течение нескольких часов.",
    "You are logged in!": "Вы вошли в систему!",
    "You are receiving this email because we received a password reset request for your account.": "Вы получили это письмо, потому что мы получили запрос на сброс пароля для Вашей учетной записи.",
    "Whoops!": "Упс!",
    "Your email address is not verified.": "Ваш email адрес не подтвержден.",
    "Whoops, something went wrong on our servers.": "Упс, на наших серверах что-то пошло не так."
  }

Роли

Для настройки груп/ролей используется файл app/Providers/AuthServiceProvider.php

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг