Как мне автоматически выбрать настроенного поставщика удостоверений SAML в многопользовательской среде для единого входа с использованием Spring SAML

Я использую Spring SAML в мультитенантном приложении для обеспечения единого входа. Разные клиенты используют разные URL-адреса для доступа к приложению, и для каждого настроен отдельный поставщик удостоверений. Как мне автоматически назначить правильного поставщика удостоверений с учетом URL-адреса, используемого для доступа к приложению?

Пример:

Клиент 1: http://tenant1.myapp.com

Клиент 2: http://tenant2.myapp.com

Я увидел, что могу добавить параметр idp к URL-адресу (http://tenant1.myapp.com?idp=my.idp.entityid.com), а SAMLContextProvider выберет поставщика удостоверений с этим идентификатором объекта. Я разработал MetadataProvider на основе базы данных, который принимает имя хоста клиента в качестве параметра инициализации для получения метаданных для этого клиента из базы данных, связанной с этим именем хоста. Теперь я думаю, что мне нужен способ перебрать поставщиков метаданных, чтобы связать entityId метаданных с именем хоста. Однако я не понимаю, как мне получить entityId метаданных. Это решило бы мою проблему.


person MarcFasel    schedule 20.01.2015    source источник


Ответы (2)


Вы можете увидеть, как анализировать доступные идентификаторы сущностей из MetadataProvider in метода _ 2 _ . Обратите внимание, что обычно каждый провайдер может предоставить несколько определений IDP и SP, а не только одно.

В качестве альтернативы вы можете дополнительно расширить ExtendedMetadataDelegate своим собственным классом, включить любые дополнительные метаданные (например, entityId) по вашему желанию, а затем просто повторно ввести MetadataProvider в свой настроенный класс и получать информацию оттуда при повторении данных через MetadataManager.

Однако на вашем месте я бы применил немного другой подход. Я бы расширил SAMLContextProviderImpl, переопределил метод populatePeerEntityId и выполнил все сопоставления имени хоста / IDP там. См. исходный метод для подробностей.

person Vladimír Schäfer    schedule 21.01.2015
comment
Я создал свой собственный SAMLContextProvider и переопределил populatePeerIdentityId. Это отлично сработало. Когда я закончил, я понял, что SAMLContextProvider используется только во время SSO, инициированного SP. В основном мы используем систему единого входа, инициированную IDP, поэтому мне также нужно было рассказать об этом. В итоге я проверил peerEntityID входящего сообщения по идентификатору объекта IDP, который настроен для этого клиента в моем настраиваемом SAMLAuthenticationProvider. - person MarcFasel; 28.10.2015
comment
Эта функция сопоставления поставщика удостоверений и поставщика услуг является ключевой для поддержки мультитенантности. Планируется ли это в следующих выпусках? - person MarcFasel; 28.10.2015
comment
Посмотрим, проект зависит от моего свободного времени (его никто не спонсирует) и его не так много. Я бы хотел заняться улучшением мультитенантности. - person Vladimír Schäfer; 28.10.2015

На момент написания Spring SAML имеет версию 1.0.1.FINAL. Он не поддерживает мультитенантность прямо из коробки. Я нашел другой способ добиться мультиарендности помимо предложений Владимира выше. Это очень просто и понятно и не требует расширения каких-либо классов Spring SAML. Кроме того, он использует встроенную в Spring SAML обработку псевдонимов в CachingMetadataManager.

В вашем контроллере захватите имя клиента из запроса и создайте объект ExtendedMetadata, используя имя клиента в качестве псевдонима. Затем создайте ExtendedMetadataDelegate из ExtendedMetadata и инициализируйте его. Извлеките из него идентификаторы сущностей и проверьте, существуют ли они в MetadataManager. Если они не существуют, добавьте поставщика и обновите метаданные. Затем получите идентификатор объекта из MetadataManager, используя getEntityIdForAlias().

Вот код контроллера. Есть встроенные комментарии, объясняющие некоторые предостережения:

@Controller
public class SAMLController {

    @Autowired
    MetadataManager metadataManager;

    @Autowired
    ParserPool parserPool;

    @RequestMapping(value = "/login.do", method = RequestMethod.GET)
    public ModelAndView login(HttpServletRequest request, HttpServletResponse response, @RequestParam String tenantName)
                                                        throws MetadataProviderException, ServletException, IOException{
        //load metadata url using tenant name
        String tenantMetadataURL = loadTenantMetadataURL(tenantName);

        //Deprecated constructor, needs to change
        HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(tenantMetadataURL, 15000);
        httpMetadataProvider.setParserPool(parserPool);

        //Create extended metadata using tenant name as the alias
        ExtendedMetadata metadata = new ExtendedMetadata();
        metadata.setLocal(true);
        metadata.setAlias(tenantName);

        //Create metadata provider and initialize it
        ExtendedMetadataDelegate metadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, metadata);
        metadataDelegate.initialize();

        //getEntityIdForAlias() in MetadataManager must only be called after the metadata provider
        //is added and the metadata is refreshed. Otherwise, the alias will be mapped to a null
        //value. The following code is a roundabout way to figure out whether the provider has already
        //been added or not. 

        //The method parseProvider() has protected scope in MetadataManager so it was copied here         
        Set<String> newEntityIds = parseProvider(metadataDelegate);
        Set<String> existingEntityIds = metadataManager.getIDPEntityNames();

        //If one or more IDP entity ids do not exist in metadata manager, assume it's a new provider.
        //If we always add a provider without this check, the initialize methods in refreshMetadata()
        //ignore the provider in case of a duplicate but the duplicate still gets added to the list
        //of providers because of the call to the superclass method addMetadataProvider(). Might be a bug.
        if(!existingEntityIds.containsAll(newEntityIds)) {
            metadataManager.addMetadataProvider(metadataDelegate);
            metadataManager.refreshMetadata();
        }

        String entityId = metadataManager.getEntityIdForAlias(tenantName);

        return new ModelAndView("redirect:/saml/login?idp=" + URLEncoder.encode(entityId, "UTF-8"));
    }

    private Set<String> parseProvider(MetadataProvider provider) throws MetadataProviderException {
        Set<String> result = new HashSet<String>();

        XMLObject object = provider.getMetadata();
        if (object instanceof EntityDescriptor) {
            addDescriptor(result, (EntityDescriptor) object);
        } else if (object instanceof EntitiesDescriptor) {
            addDescriptors(result, (EntitiesDescriptor) object);
        }

        return result;

    }

    private void addDescriptors(Set<String> result, EntitiesDescriptor descriptors) throws MetadataProviderException {
        if (descriptors.getEntitiesDescriptors() != null) {
            for (EntitiesDescriptor descriptor : descriptors.getEntitiesDescriptors()) {
                addDescriptors(result, descriptor);
            }
        }

        if (descriptors.getEntityDescriptors() != null) {
            for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) {
                addDescriptor(result, descriptor);
            }
        }
    }

    private void addDescriptor(Set<String> result, EntityDescriptor descriptor) throws MetadataProviderException {
        String entityID = descriptor.getEntityID();
        result.add(entityID);
    }
}

Я считаю, что это напрямую решает проблему OP по выяснению того, как получить IDP для данного арендатора. Но это будет работать только для IDP с одним идентификатором объекта.

person Pakman    schedule 19.05.2016
comment
Просто хочу отметить, что это решение не работает в кластерной среде, если у вас нет липких сеансов для ваших пользователей .... Первоначальный запрос к /login.do добавляет поставщика метаданных к JVM, связанной с этим запросом, однако пользователь может вернуться в приложение на другой JVM, которая не знает IDP, запустившего процесс аутентификации ... - person danw; 06.10.2017