Данная статья является продолжением серии материалов по внедрению Keycloak в качестве провайдера авторизации. [спойлер: в конце вас ждет разочарование]
В прошлый раз мы настроили ABAC (Attribute Based Access Control) с использованием Keycloak, теперь реализуем проверку разрешений на уровне приложения.
Есть различные способы реализовать контроль доступа с использованием Keycloak на уровне вашего приложения, например использование средств Spring Security, как описано в статье от Baeldung. Однако в нашем случае не получится ограничиться исключительно группами / ролями пользователя так как требуется также учитывать его кастомные атрибуты доступа.
В прошлой части статьи мы добились возможности проверять разрешение пользователя через интерфейс Keycloak. Теперь нам нужно проделать то же самое, только используя API. Реализовав логику проверки разрешений с помощью API Keycloak мы сможем затем инкапсулировать ее в Spring Interceptor (перехватчик запросов).
Проверка разрешения представляет собой следующий алгоритм:
Извлекаем из запроса URL;
Используя URL получаем идентификатор ресурса, ассоциированного с нашим URL;
Используя идентификатор ресурса и токен авторизации, проверяем разрешение для конкретного пользователя.
![Схема проверки разрешения Схема проверки разрешения](https://habrastorage.org/getpro/habr/upload_files/dfe/0c4/759/dfe0c475914db8da7787bc03289f70a0.png)
Для того чтобы получить идентификатор ресурса мы можем воспользоваться следующим запросом:
curl --location --request GET $KEYCLOAK_HOST/realms/$REALM_NAME/authz/protection/resource_set?uri=/api/me' \
--header 'Authorization: Bearer $KEYCLOAK_TEC_USER_TOKEN'
Немного подробнее про конечную точку resource_set можно прочитать тут.
Чтобы проверить доступ к конкретному ресурсу нам понадобится та же конечная точка, что и для получения токена, однако со специфичным набором параметров:
curl --location --request POST '$KEYCLOAK_HOST/realms/$REALM_NAME/protocol/openid-connect/token/' \
--header 'Authorization: Bearer$KEYCLOAK_TEC_USER_TOKEN ' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket' \
--data-urlencode 'audience=$CLIENT_NAME' \
--data-urlencode 'response_mode=decision' \
--data-urlencode 'permission=$RESOURCE_ID'
Подробнее про проверку разрешения на конкретный ресурс, можно почитать здесь.
Признаюсь честно, в конечном итоге я отказался от данной архитектуры, и на то был ряд причин:
Увеличение трафика в несколько раз, на каждый авторизованный запрос теперь приходится 2 дополнительных запроса в Keycloak, что в свою очередь замедляет систему и порождает существенные требования по RPS для инсталляции Keycloak.
Сопровождение и миграции. Сопровождение политик написанных на JS оказалось чересчур трудоемким и повлекло существенные накладные расходы, более того, на момент реализации данного решения Keycloak не располагал средствами миграции, иными словами мне не удалось реализовать надежный способ миграции состояния Keycloak (ресурсы, разрешения, политики, мапперы и тд), который можно было бы встроить в СI/CD, что в свою очередь частично обесценило хранение настроек Keycloak в коде.
В конечном итоге было принято решение оставить ABAC на стороне приложения, а Keycloak использовать только для Identity пользователей.