Photo by FLY:D on Unsplash
Photo by FLY:D on Unsplash

Иногда разработчику нужно в кратчайшие сроки разработать некий минимальный функционал, отдельно от основного приложения, ведь так быстрее и нет всей этой бюрократии с релизами код ревью, согласованиями и месяцами тестирования.

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

В любом случае было бы не ответственно оставлять любой сервис предназначенный только для внутреннего использования, доступным публично. Ведь мы никогда наперед не знаем, как и с какой целью мы можем стать целью для потенциального агента “Смитта”.


Самыми простыми способами защиты являются:

  • Развернуть приложение внутри приватной сети, доступной лишь из под VPN.

  • Basic Auth.

  • Authentication token.

  • Normal auth with registration & login.

  • Corporate SSO (like google auth).

У каждого из этих способов есть свои плюсы и минусы в зависимости от того, в какой ситуации вы находитесь в текущий момент.

Я не буду описывать в этой статье плюсы и минусы каждого. Предположим что мы остановились на варианте с авторизацией по токену, по следующим причинам:

  • Мы хотим чтоб доступ был сотрудникам которые не под VPN.

  • У нас нет SSO(а если и был бы, то лень прикручивать ее к мелкому сервису написанному на коленке).

  • Делать полноценную регистрацию нет смысла так как нет ресурсов на модерацию.


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

Перейдем наконец к кодированию.

В нашем случае будет использован PHP 8.1 & symfony 6 & doctrine & security.

Также для удобства также будет использован бандл maker.

0. Для создания рабочей среды удобно использовать Docker. Вы можете воспользоваться заготовкой для создания полноценного локального окружения — Link.

  1. Устанавливаем фреймворк.

Полноценная инструкция на сайте самого Symfony https://symfony.com/doc/current/setup.html

  1. Устанавливаем зависимости: security, doctrine, maker(dev).

composer require doctrine

composer require symfony/security-bundle

composer require symfony/maker-bundle — dev

  1. Создаем модель пользователя.

./bin/console make:user
The name of the security user class (e.g. User) [User]:
 > User
Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
 > yes
Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
 > username
Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).
Does this app need to hash/check user passwords? (yes/no) [yes]:
 > no
 created: src/Entity/User.php
 created: src/Repository/UserRepository.php
 updated: config/packages/security.yaml
Success!

4. Создадим и выполним миграцию для того чтоб в Базе данных появилась таблица пользователя:

./bin/console make:migration./bin/console doctrine:migrations:migrate

Теперь в нашей базе данных должны появится следующие 2 таблицы:

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

// config/packages/security.yaml
security:
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
        App\Entity\User:
            algorithm: auto
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: token
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            stateless: true
            provider: app_user_provider
            entry_point: App\Security\AuthenticationEntryPoint
            access_denied_handler: App\Security\AccessDeniedHandler
            custom_authenticators:
                - App\Security\ApiKeyAuthenticator
    access_control:
        - { path: ^/public, roles: PUBLIC_ACCESS }
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

6. Теперь создадим необходимые классы в директории Security:

Ключ авторизации будет считываться с заголовков запроса, но если вам нужно иная реализация(например, из GET параметров) вы легко можете это изменить в методе authenticate. Название заголовка в котором ожидается токен авторизации, хранится в константе — HEADER_AUTH_TOKEN.

Далее мы создадим класс, который отвечает за генерацию ответа в случае если авторизация не пройдена.

Последний класс отвечает за генерацию ответов в случае если пользователь авторизован, но у него недостаточно прав на совершение запрашиваемого действия.

7. Далее нам предстоит проверить работу нашей конфигурации. Для этого создадим контроллер с несколькими методами.

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

INSERT INTO demo.user (id, username, roles, token) VALUES (1, 'tester', '["ROLE_USER"]', 'super-secure-token');

9. Готово! Протестируем работу сервиса(я предпочитаю — Postman):


Вот и все, теперь ваше приложение защищено, как минимум от краулеров, а также от любопытных глаз и начинающих хакеров из отдела тех поддержки и бухгалтерии $). Также вы можете выдавать уникальные ключи доступа разным группам сотрудников или вообще каждому отдельный и при желании настраивать уровни доступа основываясь на роли пользователя.

PS: В данной статье токен хранится в БД в не зашифрованном виде, но такой подход не рекомендуется использовать, т.к. данные пользователей могут оказаться под угрозой, более подробно можно почитать — тут.

Буду рад ответить на вопросы, а также написать на интересующие вас темы касательно веб-разработки.

Ссылка на репозиторий — Link.

Комментарии (3)


  1. tsabiy
    05.06.2022 13:42

    Замочек тяжеловат надеюсь)


  1. jarrus
    05.06.2022 13:42

    "рзработчику" "бюрокртии" "испольхования" - вычитайте хотя бы статью перед публикацией или в ворд закиньте, чтобы он проверил правописание, пожалуйста


    1. VladVerpeta Автор
      05.06.2022 13:43

      извините, всегда нервничаю перед публикацией и публикую не перечитывая.
      Поправлю)