Рубрики
Без рубрики

Пользовательские Правила безопасности Micronaut

Micronaut поставляется с несколькими полезными правилами безопасности, такими как фильтрация ip-адресов, сопоставление шаблонов URL-адресов и an… С тегами micronaut, java, безопасность, jwt.

Micronaut поставляется с несколькими полезными правилами безопасности, такими как фильтрация ip-адресов, сопоставление шаблонов URL-адресов и правило проверки аннотаций. Однако их расширение за счет создания пользовательских правил также возможно и полезно для блокировки вашего приложения и уменьшения объема кода, необходимого для проверки авторизации.

Во-первых, давайте рассмотрим настройку правил по умолчанию в Micronaut security:

Настройка @Защищенный

Обычный способ защитить конечные точки – использовать @Защищенный с именем роли или ролей, например @Защищенный ("АДМИНИСТРАТОР") . Это приведет к поиску строки “АДМИНИСТРАТОР” в ключе ролей в утверждениях. Любой

{
    "sub":"1",
    "roles":["ADMIN"]
}

или

{
    "sub":"1",
    "roles": "ADMIN"
}

было бы позволено.

Чтобы настроить это, вы можете либо изменить ключ, который ищет поиск ролей по умолчанию, установив свойство micronaut.security.token.Имя роли или реализовать io.micronaut.security.токен. Поиск ролей в новом компоненте.

Пользовательские Правила безопасности

Чтобы реализовать новое пользовательское правило безопасности, вам необходимо реализовать io.micronaut.security.правила. Правило безопасности которое имеет только один метод: Проверка результатов правила безопасности (запрос HttpRequest, @Nullable RouteMatch Routematch, @Nullable Утверждения карты); SecurityRule также реализует Заказанный таким образом, вы можете переопределить метод по умолчанию getOrder , чтобы определить, когда будет запущено ваше правило.

При реализации метода проверки вам необходимо вернуть Результат правила безопасности . Это перечисление с 3 значениями: РАЗРЕШЕНО , ЗАПРЕЩЕНО и НЕИЗВЕСТНЫЙ . Вы должны вернуться РАЗРЕШЕНО если ваше правило явно разрешает действие, отказано если ваше правило специально не разрешает действие и НЕИЗВЕСТНО если у вас недостаточно информации для принятия решения. Возврат НЕИЗВЕСТНО позволяет другим правилам безопасности также попытаться принять решение, а не сразу отклонять запрос, если информации недостаточно.

При реализации правила вам передаются 3 параметра: запрос, соответствие маршрута и утверждения. Запрос – это просто запрос, сделанный клиентом, который проходит авторизацию. Routematch – это то, что дает вам доступ к методу в вашем контроллере, чтобы вы могли получать аннотации или аргументы. Претензии – это карта JSON из JWT, отправленного в запросе. Это может быть значение null, если не было JWT, или его нельзя было проанализировать или проверить.

Итак, когда вам понадобится специальное правило безопасности?

Разрешения на определенные ресурсы

Если вы хотите реализовать разрешения для определенных ресурсов, вам необходимо реализовать пользовательское правило. Это полезно, если, например, ваша система является мультитенантной и пользователям разрешено управлять только своим клиентом. Это отличается от списка ролей, потому что пользователю разрешено делать что-то только на определенном ресурсе. Обычно JWT с разрешениями для конкретных ресурсов будут выглядеть следующим образом:

    {
      "sub": "",
      "https://your-domain.com/claims": {
        "123": "ADMIN",
        "234": "READ_ONLY"
      }
    } 

Разрешения находятся в карте пространства имен (пространство имен по домену – это обычный способ сделать это – Auth0 объясняет немного больше об этом ), где ключи являются идентификаторами ресурсов. Следуя моему примеру, 123 и 234 будут идентификаторами двух арендаторов. Пользователь, запросивший этот JWT, сможет выполнять действия администратора для арендатора с идентификатором 123 и просматривать только арендатора с идентификатором 234.

Итак, как мы можем авторизовать этого пользователя в Mirconaut?

Вам понадобятся два класса, одна аннотация и одно Правило безопасности реализация. В аннотации необходимо будет указать имя идентификатора ресурса и разрешение он ищет:

public @interface RequiredPermission {

    /**
     * The name of the parameter in the controller method which contains the 
     * resource ID this permisssion is required for
     * @return resourceIdName
     */
    String resourceIdName();

    /**
     * The permission required, e.g. READ_ONLY, ADMIN, WRITE
     * @return permission
     */
    String permission();

}

Затем мы определяем правило безопасности, которое ищет это разрешение и использует значения в аннотации, чтобы извлечь идентификатор ресурса в URL-адресе и сравнить его с утверждениями в токене:

import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.http.HttpRequest;
import io.micronaut.security.rules.SecurityRule;
import io.micronaut.security.rules.SecurityRuleResult;
import io.micronaut.web.router.MethodBasedRouteMatch;
import io.micronaut.web.router.RouteMatch;

import javax.annotation.Nullable;
import javax.inject.Singleton;
import java.util.Map;
import java.util.Optional;

@Singleton
public class PermissionSecurityRule implements SecurityRule {
    @Override
    public SecurityRuleResult check(HttpRequest request, @Nullable RouteMatch routeMatch, @Nullable Map claims) {
        if (routeMatch instanceof MethodBasedRouteMatch) {
            MethodBasedRouteMatch methodBasedRouteMatch = (MethodBasedRouteMatch) routeMatch;
            if (methodBasedRouteMatch.hasAnnotation(RequiredPermission.class)) {
                AnnotationValue requiredPermissionAnnotation = methodBasedRouteMatch.getAnnotation(RequiredPermission.class);
                // Get parameters from annotation on method
                Optional resourceIdName = requiredPermissionAnnotation.stringValue("resourceIdName");
                Optional permission = requiredPermissionAnnotation.stringValue("permission");
                if (permission.isPresent() && resourceIdName.isPresent() && claims != null) {
                    // Use name of parameter to get the value passed in as an argument to the method
                    String resourceId = methodBasedRouteMatch.getVariableValues().get(resourceIdName.get()).toString();
                    // Get claim from jwt using the resource ID
                    Object permissionForResource = ((Map) claims.get("https://your-domain.com/claims")).get(resourceId);
                    if (permissionForResource != null && permissionForResource.equals(permission.get())) {
                        // if the permission exists and it's equal, allow access
                        return SecurityRuleResult.ALLOWED;
                    }
                }
            }
        }
        return SecurityRuleResult.UNKNOWN;
    }
}

Аннотирование этого правила с помощью @Singleton означает, что оно будет подхвачено Micronaut и выполнено как часть списка правил. Обратите внимание, как один единственный путь в коде приводит к возвращению метода РАЗРЕШЕНО , а все остальные возвращают НЕИЗВЕСТНО , что означает, что также можно проверить другие правила – если все возвращает НЕИЗВЕСТНО , запрос будет отклонен с 403.

Чтобы поставить это правило перед другими правилами, реализуйте метод getOrder и возвращайте число, меньшее, чем числа, возвращаемые из других правил. В настоящее время это порядок правил, и числа, возвращаемые из get Order():

  1. Правило шаблонов Ip – Номер заказа: -300
  2. Защищенное правило аннотации – Номер заказа: -200
  3. Правило конфигурации InterceptUrlMap – Номер заказа: -100
  4. Правило чувствительной конечной точки – Порядковый номер: 0

Вы можете иметь гораздо более сложные утверждения JWT и все равно проверять их правильность. Например, у меня, вероятно, был бы список разрешений в JWT для каждого идентификатора ресурса, например {"123":["ТОЛЬКО ДЛЯ ЧТЕНИЯ", "ЗАПИСЬ", "АДМИНИСТРАТОР"]} и поэтому я бы проверил, что требуемое разрешение есть в списке. Кроме того, если ваши идентификаторы не являются глобально уникальными, я бы поместил их во вложенный объект, чтобы показать, к какому ресурсу они относятся, например {"https://your-domain.com/claims ": {"арендаторы": {"123": ["ТОЛЬКО ДЛЯ ЧТЕНИЯ"]}}} .

Вывод

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

Оригинал: “https://dev.to/philhardwick/custom-micronaut-security-rules-616”