2014-05-13 8 views
5

Mam zamiar zastąpić komponent bean środowiska używany przez program Spring z własną implementacją. Czy to jest zła praktyka, a jeśli nie, jak mogę to zrobić w czysty sposób? Obecnie utworzyłem komponent bean, który implementuje interfejs Środowisko i używa istniejącego komponentu bean Środowisko, ale oznacza to, że cały kod konfiguracyjny wymagający komponentu bean Środowisko musi teraz korzystać z niestandardowego komponentu bean Środowisko. Sądzę, że czystsze byłoby zastąpienie sprężyny Springs Environment moją własną, wtedy żadna konfiguracja, która tego wymaga, nie musiałaby się zmieniać. Obecnie jedyny sposób, w jaki mogę to zrobić, to albo utworzyć mój własny kontekst aplikacji, ustawiając w ten sposób środowisko na własne potrzeby, albo mieć coś z ApplicationContextAware i ustawić tam środowisko. Oba wydają mi się trochę hokey.Spring: Jak zastąpić komponent bean Environment utworzony w kontekście aplikacji

Ograniczenia:

  • Używam najnowszej wersji Spring3.
  • Używam konfiguracji opartej na języku Java; nie XML

Dziękuję.

Edycja: tła

Chyba powinienem wyjaśnić, dlaczego chcę to zrobić w przypadku moje myślenie jest błędne. Unikałem tego, aby uniknąć niekonstruktywnego "dlaczego miałbyś to robić?" odpowiedzi.

W fasoli Wiosenne środowisko podczas wyszukiwania wartości właściwości używany jest zestaw źródeł właściwości. Typowym stos wygląda tak (ale nie tylko):

  • JNDI
  • Właściwości systemu (ustawiany za pomocą -Dmyprop = foo)
  • Zmienne środowiskowe
  • ...

Ze względów bezpieczeństwa konieczne jest zaszyfrowanie niektórych z tych właściwości (np. Haseł do baz danych). Rozwiązaniem jest użycie Jasiupt do szyfrowania właściwości. Jednak Spring/Jasypt zapewniają tylko środek do umieszczenia nowego źródła własności w środowisku. Więc:

  • plik nieruchomości z potencjalnie zaszyfrowanych wartości
  • JNDI
  • Właściwości systemu (ustawiany za pomocą -Dmyprop = foo)
  • Zmienne środowiskowe
  • ...

jednak nie jest to idealne, ponieważ oznacza to, że właściwości mogą być przechowywane tylko w jednym pliku, który ma być utrzymywany przez grupę operacyjną, lub że właściwości będą rozłożone między t pliki właściwości, zmienne środowiskowe itp. Ponadto uważam, że właściwości mogą potencjalnie zostać zaszyfrowane bez względu na ich źródło własności.

To doprowadziło mnie do myślenia, że ​​albo trzeba odszyfrować właściwości w moim kodzie, gdziekolwiek próbuję uzyskać do nich dostęp ze środowiska, albo muszę utworzyć własny komponent środowiskowy, który może to zrobić dla mnie.

Zachęcam do wysłuchania wypowiedzi konstruktywnych i alternatywnych.

EDIT: Dodawanie rozwiązanie oparte na odpowiedź od M. Deinum

public class EnvironmentBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 

    private static final String CONFIGURATION_PROPERTY_PBE_ALGORITHM = "PBE_ALGORITHM"; 
    private static final String CONFIGURATION_PROPERTY_PBE_PASSWORD = "PBE_PASSWORD"; 

    @Override 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
     StandardEnvironment environment = (StandardEnvironment) beanFactory.getBean("environment"); 

     if (environment != null) { 
      StringEncryptor encryptor = this.getEncryptor(environment); 

      MutablePropertySources mutablePropertySources = environment.getPropertySources(); 

      for (PropertySource<?> propertySource : mutablePropertySources) { 
       mutablePropertySources.replace(
         propertySource.getName(), 
         new EncryptablePropertySourcePropertySource(propertySource.getName(), propertySource, encryptor)); 
      } 
     } 
    } 

    private StringEncryptor getEncryptor(Environment environment) { 
     StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); 

     String algorithm = environment.getProperty(CONFIGURATION_PROPERTY_PBE_ALGORITHM); 
     if (algorithm != null) { 
      encryptor.setAlgorithm(algorithm); 
     } 

     String password = environment.getProperty(CONFIGURATION_PROPERTY_PBE_PASSWORD); 
     if (password != null) { 
      encryptor.setPassword(password); 
     } 

     return encryptor; 
    } 

    private class EncryptablePropertySourcePropertySource extends PropertySource<PropertySource<?>> { 

     private StringEncryptor stringEncryptor; 

     private TextEncryptor textEncryptor; 

     public EncryptablePropertySourcePropertySource(final String name, final PropertySource<?> propertySource, final StringEncryptor encryptor) { 
      super(name, propertySource); 

      this.stringEncryptor = encryptor; 
     } 

     public EncryptablePropertySourcePropertySource(final String name, final PropertySource<?> propertySource, final TextEncryptor encryptor) { 
      super(name, propertySource); 

      this.textEncryptor = encryptor; 
     } 

     @Override 
     public Object getProperty(String name) { 
      Object value = this.source.getProperty(name); 

      if (value != null && value instanceof String) { 
       value = this.decode((String) value); 
      } 

      return value; 
     } 

     private String decode(String encodedValue) { 
      if (!PropertyValueEncryptionUtils.isEncryptedValue(encodedValue)) { 
       return encodedValue; 
      } 

      if (this.stringEncryptor != null) { 
       return PropertyValueEncryptionUtils.decrypt(encodedValue, this.stringEncryptor); 

      } 

      if (this.textEncryptor != null) { 
       return PropertyValueEncryptionUtils.decrypt(encodedValue, this.textEncryptor); 
      } 

      throw new EncryptionOperationNotPossibleException(
        "Neither a string encryptor nor a text encryptor exist " 
        + "for this instance of EncryptableProperties. This is usually " 
        + "caused by the instance having been serialized and then " 
        + "de-serialized in a different classloader or virtual machine, " 
        + "which is an unsupported behaviour (as encryptors cannot be " 
        + "serialized themselves)"); 
     } 

    } 
} 
+2

Co próbujesz osiągnąć przez zwinięcie własnego "środowiska", którego nie można osiągnąć przy użyciu standardowych wiosennych rzeczy? – geoand

+0

Widzę 'Metoda' setEnvironment() 'AbstractApplicationContext' jest publiczna. Tak więc można uzyskać kontekst aplikacji z komponentem bean implementującym "ApplicationContextAware", a stamtąd można ustawić własne środowisko. Nie próbowałem tego, ale myślę, że to byłaby pierwsza rzecz, którą chciałbym spróbować. –

+0

@geo i zobacz moją edycję. – loesak

Odpowiedz

2

Jednak Wiosna/Jasypt jedynie zapewnić środki wstawiania nowego źródła własności do środowiska.

Właściwie to tam, gdzie się mylisz, możesz również wymienić PropertySources. Zobacz javadoc z MutablePropertySources. Jest to również używane wewnętrznie przez Spring, aby najpierw dodać manekina PropertySources i zastąpić je później.

Co można zrobić, to utworzyć PropertySource, który przenosi się do innego PropertySource i odszyfrowuje wartość w locie. W ten sposób możesz zamienić cały kod PropertySources na taki, który zawija się wokół oryginału.

MutablePropertySources mps = env.getgetPropertySources(); 
for (PropertySource ps : env.getgetPropertySources()) { 
    EncryptablePropertySource eps = new EncryptablePropertySource(ps.getName(), ps, encryptor); 
    mps.replace(ps.getName(), eps); 
} 

Można użyć EncryptablePropertiesPropertySource jako próbki, co w zasadzie musisz zrobić, to wymienić Properties z PropertySource.

Byłoby łatwiej, gdyby ustalono, że SPR-8928 będzie zarejestrowany, a rejestracja wszystkich właściwości zostanie zmieniona i nie będziesz potrzebować tych hacków.

+0

Dziękuję.Obawiałem się, że mogłem się mylić i miałem taką nadzieję. Zajrzę do tego i jeśli ta właściwa aleja odpowiednio oznaczy odpowiedź. – loesak

+0

Gdy się nad tym zastanawiam, uważam, że jest to odpowiedni sposób, aby to zrobić, dopóki ten błąd nie zostanie naprawiony. Nie mam pojęcia, jak to zrobić, zanim jakikolwiek komponent wymagający środowiska? – loesak

+0

myślenie BeanFactoryPostProcessor – loesak

Powiązane problemy