Мини-блог на Laravel, часть 9. Защита маршрутов создания, редактирования и удаления
27.09.2020
Теги: Laravel • MySQL • PHP • Web-разработка • Авторизация • Аутентификация • БазаДанных • Блог • Пользователь • ПраваДоступа • Практика • Фреймворк
Чтобы предоставить доступ к определённым роутам только аутентифицированным пользователям, можно использовать посредник (middleware
). Laravel поставляется с посредником auth
, который определён в Illuminate\Auth\Middleware\Authenticate
. Когда посредник определяет, что пользователь не аутентифицирован, он перенаправляет его на роут с именем login
. Мы можем изменить это поведение в методе redirectTo
класса app/Http/Middleware/Authenticate.php
.
<?php namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; class Authenticate extends Middleware { /** * Get the path the user should be redirected to when they are not authenticated. * * @param \Illuminate\Http\Request $request * @return string|null */ protected function redirectTo($request) { if (! $request->expectsJson()) { return route('login'); } } }
Защита маршрутов
Давайте защитим наши маршруты создания, редактирования и удаления поста.
<?php use Illuminate\Support\Facades\Route; Route::get('/', 'PostController@index')->name('blog.index'); Route::get('post/index', 'PostController@index')->name('post.index'); Route::get('post/search', 'PostController@search')->name('post.search'); Route::get('post/create', 'PostController@create')->name('post.create')->middleware('auth'); Route::post('post/store', 'PostController@store')->name('post.store')->middleware('auth'); Route::get('post/show/{id}', 'PostController@show')->name('post.show'); Route::get('post/edit/{id}', 'PostController@edit')->name('post.edit')->middleware('auth'); Route::patch('post/update/{id}', 'PostController@update')->name('post.update')->middleware('auth'); Route::delete('post/destroy/{id}', 'PostController@destroy')->name('post.destroy')->middleware('auth'); Auth::routes();
Второй способ защитить маршруты — добавить посредник auth
в конструктор контроллера.
class PostController extends Controller { /* ... */ public function __construct() { // не аутентифицированнные пользователи могут только просматривать $this->middleware('auth')->except('index', 'show', 'search'); } /* ... */ }
Определить, что пользователь уже вошёл в систему, можно с помощью методов check()
и guest()
фасада Auth
или хелпера auth()
.
use Illuminate\Support\Facades\Auth; if (Auth::check()) { // Пользователь вошёл в систему... }
if (auth()->guest()) { // Пользователь не вошёл в систему... }
Права доступа
Теперь добавлять, редактировать и удалять посты блога могут только аутентифицированнные пользователи. Но плохо, что все пользователи могут видеть кнопки «Редактировать» и «Удалить». Давайте это исправим — для этого редактируем шаблон show.blade.php
.
@extends('layouts.site', ['title' => $post->title]) @section('content') <div class="row"> <div class="col-12"> <div class="card mt-4 mb-4"> <div class="card-header"> <h1>{{ $post->title }}</h1> </div> <div class="card-body"> <img src="{{ $post->image ?? asset('img/default.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">{{ $post->body }}</p> </div> <div class="card-footer"> <div class="clearfix"> <span class="float-left"> Автор: {{ $post->author }} <br> Дата: {{ date_format($post->created_at, 'd.m.Y H:i') }} </span> <span class="float-right"> @auth <!-- Только аутентифицированные пользователи могут редактировать и удалять --> @if (auth()->id() == $post->author_id) <!-- …причем, только свои посты блога --> <a href="{{ route('post.edit', ['id' => $post->post_id]) }}" class="btn btn-dark mr-2">Редактировать</a> <!-- Форма для удаления поста --> <form action="{{ route('post.destroy', ['id' => $post->post_id]) }}" method="post" onsubmit="return confirm('Удалить этот пост?')" class="d-inline"> @csrf @method('DELETE') <input type="submit" class="btn btn-danger" value="Удалить"> </form> @endif @endauth </span> </div> </div> </div> </div> </div> @endsection
Но это еще не все — посты блога может редактировать и удалять только автор. Так что вносим изменения в методы контроллера.
<?php namespace App\Http\Controllers; use App\Http\Requests\PostRequest; use App\Post; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image; class PostController extends Controller { /* ... */ public function __construct() { // не аутентифицированнные пользователи могут только просматривать $this->middleware('auth')->except('index', 'show', 'search'); } /* ... */ public function edit($id) { $post = Post::findOrFail($id); // пользователь может редактировать только свои посты if (Auth::id() != $post->author_id) { return redirect() ->route('post.index') ->withErrors('Вы можете редактировать только свои посты'); } return view('posts.edit', compact('post')); } /* ... */ public function update(PostRequest $request, $id) { $post = Post::findOrFail($id); // пользователь может редактировать только свои посты if (Auth::id() != $post->author_id) { return redirect() ->route('post.index') ->withErrors('Вы можете редактировать только свои посты'); } $post->title = $request->input('title'); $post->excerpt = $request->input('excerpt'); $post->body = $request->input('body'); // если надо удалить старое изображение if ($request->input('remove')) { $this->removeImage($post); } // если было загружено новое изображение $this->uploadImage($request, $post); // все готово, можно сохранять пост в БД $post->update(); return redirect() ->route('post.show', compact('id')) ->with('success', 'Пост успешно отредактирован'); } /* ... */ public function destroy($id) { $post = Post::findOrFail($id); // пользователь может удалять только свои посты if (Auth::id() != $post->author_id) { return redirect() ->route('post.index') ->withErrors('Вы можете удалять только свои посты'); } $this->removeImage($post); $post->delete(); return redirect() ->route('post.index') ->with('success', 'Пост был успешно удален'); } }
И еще один момент — будем устанавливать идентификатор пользователя в поле author_id
при добавлении нового поста.
class PostController extends Controller { /* ... */ public function store(PostRequest $request) { $post = new Post(); // автор поста — текущий пользователь $post->author_id = Auth::id(); $post->title = $request->input('title'); $post->excerpt = $request->input('excerpt'); $post->body = $request->input('body'); $this->uploadImage($request, $post); $post->save(); return redirect() ->route('post.index') ->with('success', 'Новый пост успешно создан'); } /* ... */ }
Администратор
Первый пользователь, который зарегистрировался, будет обладать правами администратора — сможет редактировать и удалять не только свои посты, но и посты других пользователй. Мы бы могли добавить еще одно поле admin
в таблицу базы данных users
. Но тогда надо будет и создавать какую-то админку, а у нас блог с минимальным функционалом. Так что просто будем проверять, что идентификатор администратора равен единице.
class PostController extends Controller { /* ... */ public function edit($id) { $post = Post::findOrFail($id); // проверяем права пользователя на это действие if (!$this->checkRights($post)) { return redirect() ->route('post.index') ->withErrors('У вас нет прав на это действие'); } return view('posts.edit', compact('post')); } /* ... */ public function update(PostRequest $request, $id) { $post = Post::findOrFail($id); // проверяем права пользователя на это действие if (!$this->checkRights($post)) { return redirect() ->route('post.index') ->withErrors('У вас нет прав на это действие'); } $post->title = $request->input('title'); $post->excerpt = $request->input('excerpt'); $post->body = $request->input('body'); // если надо удалить старое изображение if ($request->input('remove')) { $this->removeImage($post); } // если было загружено новое изображение $this->uploadImage($request, $post); // все готово, можно сохранять пост в БД $post->update(); return redirect() ->route('post.show', compact('id')) ->with('success', 'Пост успешно отредактирован'); } /** * Проверяет права пользователя на редактирование и удаление поста */ private function checkRights(Post $post) { return Auth::id() == $post->author_id || Auth::id() == 1; } /* ... */ public function destroy($id) { $post = Post::findOrFail($id); // проверяем права пользователя на это действие if (!$this->checkRights($post)) { return redirect() ->route('post.index') ->withErrors('У вас нет прав на это действие'); } $this->removeImage($post); $post->delete(); return redirect() ->route('post.index') ->with('success', 'Пост был успешно удален'); } }
@extends('layouts.site', ['title' => $post->title]) @section('content') <div class="row"> <div class="col-12"> <div class="card mt-4 mb-4"> <div class="card-header"> <h1>{{ $post->title }}</h1> </div> <div class="card-body"> <img src="{{ $post->image ?? asset('img/default.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">{{ $post->body }}</p> </div> <div class="card-footer"> <div class="clearfix"> <span class="float-left"> Автор: {{ $post->author }} <br> Дата: {{ date_format($post->created_at, 'd.m.Y H:i') }} </span> <span class="float-right"> @auth @if (auth()->id() == $post->author_id || auth()->id() == 1) <a href="{{ route('post.edit', ['id' => $post->post_id]) }}" class="btn btn-dark mr-2">Редактировать</a> <!-- Форма для удаления поста --> <form action="{{ route('post.destroy', ['id' => $post->post_id]) }}" method="post" onsubmit="return confirm('Удалить этот пост?')" class="d-inline"> @csrf @method('DELETE') <input type="submit" class="btn btn-danger" value="Удалить"> </form> @endif @endauth </span> </div> </div> </div> </div> </div> @endsection
Страница 404
Разместим картинку 404.jpg
в директории public/img
, где она будет доступна из веб. Создадим директорию resources/views/errors
и внутри нее — шаблон 404.blade.php
.
@extends('layouts.site', ['title' => 'Страница не найдена']) @section('content') <div class="row"> <div class="col-12"> <div class="card mt-4 mb-4"> <div class="card-header"> <h1>Страница не найдена</h1> </div> <div class="card-body"> <img src="{{ asset('img/404.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">Запрошенная страница не найдена.</p> </div> </div> </div> </div> @endsection
- Блог на Laravel 7, часть 11. Панель управления — назначение ролей и прав для пользователей
- Блог на Laravel 7, часть 4. Роли и Права пользователей, трейт HasRolesAndPermissions
- Блог на Laravel 7, часть 3. Checkbox «Запомнить меня» и подтверждение адреса почты
- Магазин на Laravel 7, часть 9. Панель управления сайтом, авторизация администратора
- Мини-блог на Laravel, часть 8. Регистрация и аутентификация пользователей на сайте
- Блог на Laravel 7, часть 17. Временная зона для пользователей, деплой на хостинг TimeWeb
- Блог на Laravel 7, часть 16. Роль нового пользователя, сообщение админу о новом посте
Поиск: Laravel • Web-разработка • Авторизация • Аутентификация • Блог • Пользователь • Права доступа • Практика • Фреймворк • База данных • MySQL • PHP