Production / Добавление авторизации

Теперь немного позанимаемся процессом публикации проекта на удалённом сервере так чтобы он был доступен с любого компьютера.

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

Было бы логично чтобы случайны пользователь не мог редактировать данные.

Для контроля доступа в приложениях принято использовать так называемую концепцию middleware. По сути это специальные обработчики который выполняется либо до непосредственной обработки запроса, либо после.

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

Давайте создадим базовый Middleware класс в папке framework

вот с таким содержимым

<?php

class BaseMiddleware {
    public function apply(BaseController $controller, array $context) {
        
    }
}

теперь идем в Router и сначала правим маршрут:

class Route {
    public string $route_regexp;
    public $controller; 
    public array $middlewareList = []; // добавил массив под middleware
    
    // метод с помощью которого будем добавлять обработчик
    public function middleware(BaseMiddleware $m) : Route {
        array_push($this->middlewareList, $m);
        return $this;
    }

    public function __construct($route_regexp, $controller)
    {
        // ...
    }
}

а теперь подкручиваем сам роутер:

class Router {
    // ...
    
    // перепиливаем функцию add таким образом
    // добавляем возвращаемый тип : Route 
    public function add($route_regexp, $controller) : Route {
        // создаем экземпляр маршрута
        $route = new Route("#^$route_regexp$#", $controller);
        array_push($this->routes, $route);
        
        // возвращаем как результат функции
        return $route;
    }

плюс переопределяем get_or_default:

class Router {
    // ...
    public function add($route_regexp, $controller) : Route {
        // ...
    }

    public function get_or_default($default_controller) {
        $url = $_SERVER["REQUEST_URI"];

        $path = parse_url($url, PHP_URL_PATH);

        $controller = $default_controller;
        $newRoute = null; // добавили переменную под маршрут

        $matches = [];
        foreach($this->routes as $route) {
            if (preg_match($route->route_regexp, $path, $matches)) {
                $controller = $route->controller;
                $newRoute = $route; // загоняем соответствующий url маршрут в переменную
                break;
            }
        }

        // ...

        // вызываем обработчики middleware, если такие есть
        if ($newRoute) {
            foreach ($newRoute->middlewareList as $m) {
                $m->apply($controllerInstance, []);
            }
        }

        return $controllerInstance->process_response();
    }

теперь добавим класс обработчик для проверки был ли пользователь авторизован. Для этого создадим папку middlewares и добавим в нее класс

загоним в него просто вывод сообщения, что необходимо авторизоваться

public function apply(BaseController $controller, array $context)
{
    echo "Авторизуйтесь пожалуйста";
}

и теперь подключим его во всех контроллерах в которых происходит некоторая манипуляция данными, у меня оказалось четыре таких контроллера:

и теперь попробуем их пооткрывать:

как видно теперь вверху выскакивает надпись. Давайте попробуем реализовать простейшую HTTP авторизацию, она делается с помощью указания специальных заголовков в запросе

class LoginRequiredMiddeware extends BaseMiddleware {
    public function apply(BaseController $controller, array $context)
    {
        // заводим переменные под правильный пароль
        $valid_user = "user";
        $valid_password = "12345";
        
        // берем значения которые введет пользователь
        $user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
        $password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
        
        // сверяем с корректными
        if ($valid_user != $user || $valid_password != $password) {
            // если не совпали, надо указать такой заголовок
            // именно по нему браузер поймет что надо показать окно для ввода юзера/пароля
            header('WWW-Authenticate: Basic realm="Space objects"');
            http_response_code(401); // ну и статус 401 -- Unauthorized, то есть неавторизован
            exit; // прерываем выполнение скрипта
        }
    }
}

получается такой вполне себе такой колхозный ограничитель неавторизованного доступа.

1
  1. Добавить в БД таблицу пользователей. В таблице должно быть не менее трех полей: id, username, password
  2. Обновить LoginRequiredMiddeware чтобы он проверял корректность введенных значений по таблице в БД. Доступ к pdo можно получить из функции apply через $controller->pdo->prepare(...)