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 в файле контекста.
Надеюсь, что это поможет.