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(...)