Взгляд на JWT как на инструмент построения стройной архитектуры приложения

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Тогда-то я впервые и применил на практике JWT, результатами чего остался очень доволен. В этой статье я хочу поделиться своим опытом работы с этим стандартом на конкретных примерах и рассказать, почему он удовлетворяет все мои потребности как разработчика в самых разных проектах: новых и долгоживущих, микросервисных и монолитных, в ситуациях, когда нужно быстро накидать MVP, и когда требуется не спеша и вдумчиво заложить основу на долгие годы качественной разработки.

Как мы обычно делаем Security

До того, как поставленная задача вынудила меня использовать JWT, я реализовывал Security по стандартному flow, описываемому в документациях фреймворков.

Как правило, первым шагом мы описываем класс User, который несет в себе атрибуты, необходимые для принятия решения о доступе. Эти атрибуты могут помещаться:

  • в плоской структуре самого класса User (просто делаем поле role и подобные поля);

  • в агрегируемых юзером классах, таких как Profile;

  • в смежных классах, ссылающихся на User, например, классе Manager, описывающем уровни и полномочия менеджеров.

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

После этого мы связываем тем или иным способом компонент фреймворка Security с нашим классом User и кодом, содержащим логику авторизации. Детали здесь уже зависят от фреймворка и подхода разработчика, но концептуально механизм выглядит плюс-минус одинаково:

  1. Бэкенд получает некие credentials: токен, пару логин-пароль или сессионные куки.

  2. По этим credentials из хранилища - чаще всего реляционной БД - извлекается инстанс нашего класса User. Этот инстанс обычно доступен через компонент фреймворка Security, то есть, в коде мы можем сделать что-то вроде $security->getLoggedUser().

  3. В зависимости от запрашиваемого действия (чаще всего это выполнение того или иного контроллера) вызывается определенный код проверки прав доступа, куда передается либо весь User, либо его отдельные атрибуты.

  4. Если код авторизации не прервал выполнение - то есть, было принято положительное решение о доступе - продолжается обычная работа приложения: выполняется запрашиваемое действие.

Недавно я писал о том, как вот такая работа с Security превращается в боль и о том, какими средствами можно этому противодействовать. Одним из таких средств как раз является JWT.

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

Два полезных свойства JWT

Когда я прочитал некоторое количество из бесчисленных материалов о том, что такое JWT, из каких частей он состоит и т. д., мне показались ключевыми два его свойства: автономность и способность нести payload.

На самом деле, речь идет о двух сторонах одной медали: JWT автономен потому, что он несет payload. Чем это нам помогает? Прежде всего тем, что JWT может и должен содержать в себе всю необходимую для принятия решения о доступе информацию. Иными словами, если ваше приложение получило JWT, оно уже не должно дергать юзера из базы или (упаси Боже) из соседнего микросервиса.

Первая выгода, которую несет автономность JWT, лежит на поверхности: уменьшается количество запросов к базе и/или в соседние микросервисы. Но, когда я начал внедрять JWT, то осознал и другую выгоду. Стандарт как бы навязал мне более правильную архитектуру.

Когда я начал думать о том, как мне сформировать payload для моего JWT, то быстро осознал: payload JWT - это не мой доменный User. Это отдельная структура, которая может содержать в себе данные не только из класса User, но и из других источников. Именно возможность вынести в JWT все, что необходимо для работы Security, ощутимо развязывает руки при проектировании нашего приложения.

Конечно, у автономности JWT есть и оборотная сторона: однажды выданный токен не может быть отозван и продолжает действовать пока не протухнет. На самом деле, это решаемая проблема. Некоторые решают ее через сохранение отозванных JWT, но это в корне неверно. Свое решение я опишу в конце статьи.

Теперь, когда мы разобрались, чем JWT так полезен, давайте я поделюсь своим опытом использования его в монолите и микросервисах.

JWT в монолите: делаем несвязанный слой Security

При проектировании Security приложения я исхожу из того, что JWT payload обязан содержать всю необходимую для принятия решения о доступе информацию. Если следовать этому правилу, то логично предположить, что нам потребуется класс, несущий эту инфромацию, и класс, собирающий ее (нет, не фабрика, фабрика - это другое, и хватит называть этим словом любой порождающий код

Источник: https://habr.com/ru/articles/882152/


Интересные статьи

Интересные статьи

Меня зовут Андрей Буйновский, я разработчик в ИТ-компании. Мы с командой решили написать эту статью и поделиться некоторыми проблемами, подводными камнями и их решениями в разработке мобильного прилож...
Недавно мне пришлось искать себе коллег. Эта статья об удивительном, но печальном опыте поиска хорошего джуна+ (React). TL;DR: увы, без тестового задания обойтись не получилось. когда не хочется, но...
C интересом прочитал статью о впечатляющих показателях  процессора Эльбрус  на алгоритме шифрования  Кузнечик.  В этой же статье приведена ссылка на проект  с реализацией указ...
Настройка любой площадки для CMS — это рутинный процесс, который должен быть доведен до автоматизма в каждой уважающей себя компании. А потому частенько воспринимается, как восход солнца — это происхо...
Каждый лишний элемент на сайте — это кнопка «Не купить», каждая непонятность или трудность, с которой сталкивается клиент — это крестик, закрывающий в браузере вкладку с вашим интернет-магазином.