Spring, файл свойств, пустые значения

Я настроил spring безопасность с сервером ldap (но продолжайте читать, это не проблема, если вы не знаете об этом, это действительно проблема spring). Все работает как шарм. Вот строка, которую я использую для этого:

<ldap-server ldif="" root="" manager-dn="" manager-password="" url=""  id="ldapServer" />

Если я заполню атрибуты ldif и root, он запустит встроенный сервер:

<ldap-server ldif="classpath://ldap.ldif" root="dc=springframework,dc=org" manager-dn="" manager-password="" url=""  id="ldapServer" />

Если я заполню другие поля, он запустит удаленный сервер:

<ldap-server ldif="" root="" manager-dn="dc=admin,dc=springframeworg,dc=org" manager-password="password" url="ldap://myldapserver.com/dc=springframeworg,dc=org" id="ldapServer" />

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

Итак, я заменяю значения атрибутов следующим образом:

<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="${ldap.server.url}"  id="ldapServer" />

и создайте файл свойств с помощью:

ldap.server.url=
ldap.server.manager.dn=
ldap.server.manager.password=

ldap.ldif.path= 
ldap.ldif.root= 

Теперь, забавная часть проблемы. Если я запишу в файл следующие свойства:

ldap.server.url=ldap://myldapserver.com/dc=springframeworg,dc=org
ldap.server.manager.dn=dc=admin,dc=springframeworg,dc=org
ldap.server.manager.password=password

ldap.ldif.path= 
ldap.ldif.root= 

Он запускает удаленный сервер, как ожидалось.

Если я заполню файл свойства следующим образом:

ldap.server.url=
ldap.server.manager.dn=
ldap.server.manager.password=

ldap.ldif.path= classpath:ldap.ldif
ldap.ldif.root= dc=springframeworg,dc=org

Он не запускается, жалуясь, что отсутствует ldap-url. Но проблема в том, что если я изменю конфигурацию spring:

<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="${ldap.server.url}"  id="ldapServer" />

to (просто удалив ссылку на переменную ${ldap.server.url})

<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url=""  id="ldapServer" />

Он работает!

Мои, тем не менее, что spring не заменяет значение атрибута конфигурацией конфигурации, если этот пуст. Но я нахожу это странным.

Можете ли вы дать мне понять, что это понимать? И что лучше всего сделать для настройки моего ldap-сервера через файл свойств?

EDIT: это связано с плохим выбором дизайна (см. принятый ответ), проблема была открыта на jira: https://jira.springsource.org/browse/SEC-1966

Ответ 1

Хорошо, я думаю, что это ошибка безопасности spring.

Если я отлаживаю и смотрю на класс LdapServerBeanDefinition, существует метод, называемый "parse". Вот выдержка:

public BeanDefinition parse(Element elt, ParserContext parserContext) {
    String url = elt.getAttribute(ATT_URL);

    RootBeanDefinition contextSource;

    if (!StringUtils.hasText(url)) {
        contextSource = createEmbeddedServer(elt, parserContext);
    } else {
        contextSource = new RootBeanDefinition();
        contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
        contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
    }

    contextSource.setSource(parserContext.extractSource(elt));

    String managerDn = elt.getAttribute(ATT_PRINCIPAL);
    String managerPassword = elt.getAttribute(ATT_PASSWORD);

    if (StringUtils.hasText(managerDn)) {
        if(!StringUtils.hasText(managerPassword)) {
            parserContext.getReaderContext().error("You must specify the " + ATT_PASSWORD +
                    " if you supply a " + managerDn, elt);
        }

        contextSource.getPropertyValues().addPropertyValue("userDn", managerDn);
        contextSource.getPropertyValues().addPropertyValue("password", managerPassword);
    }

    ...
}

Если я отлаживаю здесь, все переменные (url, managerDn, managerPassword...) не заменяются значением, указанным в файле свойств. Итак, url имеет значение ${ldap.server.url}, managerDn имеет значение ${ldap.server.manager.dn} и т.д.

Параметр метода создает bean, источник контекста, который будет использоваться дальше. И когда этот bean будет использоваться, заменители места будут заменены.

Здесь мы получили ошибку. Метод parse проверяет, является ли url пустым или нет. Проблема в том, что url здесь не пуст, потому что он имеет значение ${ldap.server.url}. Таким образом, метод parse создает источник контекста в качестве удаленного сервера.

Когда созданный источник будет использоваться, он заменит ${ldap.server.url} пустым значением (как указано в файле свойств). И....... Ошибка!

Я не знаю, как это решить на данный момент, но теперь я понимаю, почему это ошибка?)

Ответ 2

Я не могу это объяснить, но я думаю, что вы можете исправить свою проблему, используя синтаксис по умолчанию, доступный с Spring 3.0.0.RC1 (см.).

В журнале chageg вы можете прочитать: PropertyPlaceholderConfigurer поддерживает синтаксис по умолчанию "$ myKey: myDefaultValue"

В любом случае, я думаю, что проблема заключается в том, что "" является допустимым значением, но значения в файле свойств нет.

Ответ 3

Я думаю, что url="" работает, потому что атрибут url имеет тип xs:token в spring -security XSD, а пустая строка преобразуется в null (xs:token удаляет любые начальные или конечные пробелы, поэтому "" может быть признано как нет значения). Возможно, значение ${ldap.server.url} разрешено как пустая строка, и поэтому у вас есть ошибка.

Вы можете попробовать использовать профили Spring для определения различных конфигураций сервера ldap (см. Spring Блог команды для получения более подробной информации о профилях)

Ответ 4

Я считаю, что здесь есть проблема при использовании держателей мест. Наиболее вероятно, что проблема будет решена следующим образом:

Создайте класс, который расширяет свойство PropertyPlaceHolderConfigurer и переопределяет его метод convertPropertyValue() в методе вы можете вернуть свойство как пустую строку, если вы найдете что-то другое, кроме строки, которая имеет тип LDAP url i.e. ldap://myldapserver.com/dc=springframeworg,dc=org

Также вам нужно настроить новую специализацию класса PropertyPlaceHolderConfigurer в файле контекста.

Надеюсь, что это поможет.