Иногда разработчику нужно в кратчайшие сроки разработать некий минимальный функционал, отдельно от основного приложения, ведь так быстрее и нет всей этой бюрократии с релизами код ревью, согласованиями и месяцами тестирования.
Для этого разработчик создает новый репозиторий и там реализует свое “чудо”. Это может быть как автономный инструмент который считает процент прибавок к зарплате за сверхурочные в зависимости от ставки и выслуги лет, так и более сложная система с доступом корпоративной базе данных.
В любом случае было бы не ответственно оставлять любой сервис предназначенный только для внутреннего использования, доступным публично. Ведь мы никогда наперед не знаем, как и с какой целью мы можем стать целью для потенциального агента “Смитта”.
Самыми простыми способами защиты являются:
Развернуть приложение внутри приватной сети, доступной лишь из под 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.
Устанавливаем фреймворк.
Полноценная инструкция на сайте самого Symfony https://symfony.com/doc/current/setup.html
Устанавливаем зависимости: security, doctrine, maker(dev).
composer require doctrine
composer require symfony/security-bundle
composer require symfony/maker-bundle — dev
Создаем модель пользователя.
./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)
jarrus
05.06.2022 13:42"рзработчику" "бюрокртии" "испольхования" - вычитайте хотя бы статью перед публикацией или в ворд закиньте, чтобы он проверил правописание, пожалуйста
VladVerpeta Автор
05.06.2022 13:43извините, всегда нервничаю перед публикацией и публикую не перечитывая.
Поправлю)
tsabiy
Замочек тяжеловат надеюсь)