Как настроить функциональность существующего плагина Grails, изменив поведение метода doWithSpring
Я новичок в Grails и при работе с плагином Spring Security LDAP было установлено, что он принимает пароль сервера ldap только в виде обычного текста. Задача состоит в том, чтобы передать зашифрованный пароль, который расшифровывается, прежде чем он будет использован плагином во время фазы инициализации.
Я уже искал все возможные блоги и вопросы о стековом потоке, но не смог найти способ расширить основной класс плагина, чтобы просто переопределить метод doWithSpring(), чтобы я мог просто добавить необходимую логику дешифрования для пароля сервера Ldap. Любая помощь здесь будет оценена.
Я уже видел и пробовал плагин jasypt, но он также не работает, если пароль хранится во внешнем файле, а не в приложении yml. Поэтому я ищу решение расширить основной класс плагина безопасности Spring, добавить необходимое поведение и зарегистрировать пользовательский класс.
РЕДАКТИРОВАТЬ
Добавление фрагмента из плагина Grails LDAP Security, который я пытаюсь переопределить. Поэтому, если я успешно могу обновить значение объекта securityConfig до загрузки плагина, цель решена.
Несколько фрагментов из плагина:
def conf = SpringSecurityUtils.securityConfig
...
...
contextSource(DefaultSpringSecurityContextSource, conf.ldap.context.server) { // 'ldap://localhost:389'
authenticationSource = ref('ldapAuthenticationSource')
authenticationStrategy = ref('authenticationStrategy')
userDn = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**password = conf.ldap.context.managerPassword // 'secret'**
contextFactory = contextFactoryClass
dirObjectFactory = dirObjectFactoryClass
baseEnvironmentProperties = conf.ldap.context.baseEnvironmentProperties // none
cacheEnvironmentProperties = conf.ldap.context.cacheEnvironmentProperties // true
anonymousReadOnly = conf.ldap.context.anonymousReadOnly // false
referral = conf.ldap.context.referral // null
}
ldapAuthenticationSource(SimpleAuthenticationSource) {
principal = conf.ldap.context.managerDn // 'cn=admin,dc=example,dc=com'
**credentials = conf.ldap.context.managerPassword // 'secret'**
}
2 ответа
После неудачной попытки использования плагина Jasypt и решений BeanPostProcessor для моего варианта использования я обнаружил, что приведенное ниже решение работает идеально.
Чтобы снова описать формулировку проблемы здесь, а) мы должны были хранить пароли в зашифрованном формате в файлах свойств б) и, учитывая, что мы упаковывали их в файл войны, поэтому свойства не должны храниться в войне, чтобы позволить обновление сценариев автоматического развертывания зашифрованные пароли в зависимости от среды
Плагин Jasypt был идеальным решением для сценария использования а), но он не смог охватить сценарий б) Более того, плагин Grails LDAP Security загружался довольно рано, поэтому процессоры Bean Post здесь также не помогали.
Решение: Создан новый класс путем реализации интерфейса SpringApplicationRunListener. Расширил его методы и проанализировал файл свойств, используя YamlPropertySourceLoader
Образец кода:
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load(
"application.yml", new ClassPathResource("application.yml"),"default");
return applicationYamlPropertySource;
Как только свойства были загружены в объект MapPropertySource, проанализировали их для зашифрованных значений и применили логику дешифрования.
Вся эта реализация была выполнена до того, как какие-либо плагины были инициализированы во время процесса загрузки Grails.
Надеюсь, это поможет другим.
Вам не нужно переопределять doWithSpring()
метод в существующем плагине. Вы можете предоставить свой собственный плагин, который загружается после того, на который вы хотите повлиять, и ваш doWithSpring()
добавить все, что вы хотите в контекст. Если вы добавите бины с тем же именем, что и у другого плагина, вы замените те, что были предоставлены другим плагином, если вы настроите свой плагин для загрузки после другого. Точно так же вы могли бы сделать то же самое в resources.groovy
приложения, если вы не хотите писать плагин для этого.
У вас есть и другие варианты. Вы можете написать постпроцессор bean или постпроцессор определения bean, который влияет на bean, созданные другим плагином. В зависимости от подробностей, это может быть лучшей идеей.
РЕДАКТИРОВАТЬ:
После просмотра вашего комментария ниже я создал простой пример, который показывает, как вы можете использовать определение постпроцессора. Смотрите проект на https://github.com/jeffbrown/postprocessordemo.
Интересные биты:
https://github.com/jeffbrown/postprocessordemo/blob/master/src/main/groovy/demo/SomeBean.groovy
package demo
class SomeBean {
String someValue
}
package demo
import org.springframework.beans.BeansException
import org.springframework.beans.MutablePropertyValues
import org.springframework.beans.PropertyValue
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
class SomePostProcessor implements BeanDefinitionRegistryPostProcessor{
@Override
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition definition = registry.getBeanDefinition('someBean')
MutablePropertyValues values = definition.getPropertyValues()
PropertyValue value = values.getPropertyValue('someValue')
def originalValue = value.getValue()
// this is where you could do your decrypting...
values.addPropertyValue('someValue', "MODIFIED: ${originalValue}".toString())
}
@Override
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
https://github.com/jeffbrown/postprocessordemo/blob/master/grails-app/conf/spring/resources.groovy
beans = {
someBean(demo.SomeBean) {
someValue = 'Some Value'
}
somePostProcessor demo.SomePostProcessor
}
package postprocessordemo
import demo.SomeBean
class BootStrap {
SomeBean someBean
def init = { servletContext ->
log.info "The Value: ${someBean.someValue}"
}
def destroy = {
}
}
При запуске приложения вы увидите вывод журнала, который выглядит примерно так...
2017-10-23 19:04:54.356 INFO --- [ main] postprocessordemo.BootStrap : The Value: MODIFIED: Some Value
"ИЗМЕНЕНО" - это свидетельство того, что постпроцессор определения бина изменил значение свойства в бине. В моем примере я просто добавляю некоторый текст в строку. В вашей реализации вы могли бы расшифровать пароль или делать все, что вы хотите там делать.
Надеюсь, это поможет.