Загрузка пользовательского ApplicationContextInitializer при загрузке AWS Lambda Spring - PullRequest
0 голосов
/ 27 января 2019

Как загрузить собственный ApplicationContextInitializer для весенней загрузки AWS Lambda?У меня есть лямбда-приложение aws, использующее весеннюю загрузку, я хотел бы написать ApplicationContextInitializer для расшифровки паролей базы данных.У меня есть следующий код, который работает при локальном запуске в качестве приложения весенней загрузки, но когда я развертываю его на консоли AWS в качестве лямбды, он не работает.

Вот мой код 1. application.properties

spring.datasource.url=url
spring.datasource.username=testuser
CIPHER.spring.datasource.password=encryptedpassword

Следующий код - ApplicationContextInitializer, при условии, что пароль - Base64, закодированный только для тестирования (в действительности он будет зашифрован AWM KMS).Идея в том, что ключ начинается с «CIPHER».(как в CIPHER.spring.datasource.password) Я предполагаю, что его значение необходимо расшифровать, и другая пара значений ключа с фактическим ключом (здесь spring.datasource.password) и его расшифрованное значение будут добавлены при инициализации контекста.

будет похоже на spring.datasource.password=decrypted password

@Component
public class DecryptedPropertyContextInitializer 
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {

  private static final String CIPHER = "CIPHER.";

  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
      ConfigurableEnvironment environment = applicationContext.getEnvironment();
      for (PropertySource<?> propertySource : environment.getPropertySources()) {
          Map<String, Object> propertyOverrides = new LinkedHashMap<>();
          decodePasswords(propertySource, propertyOverrides);
          if (!propertyOverrides.isEmpty()) {
              PropertySource<?> decodedProperties = new MapPropertySource("decoded "+ propertySource.getName(), propertyOverrides);
              environment.getPropertySources().addBefore(propertySource.getName(), decodedProperties);
          }
      }
  }

  private void decodePasswords(PropertySource<?> source, Map<String, Object> propertyOverrides) {
    if (source instanceof EnumerablePropertySource) {
        EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) source;
        for (String key : enumerablePropertySource.getPropertyNames()) {
            Object rawValue = source.getProperty(key);
            if (rawValue instanceof String && key.startsWith(CIPHER)) {
                String cipherRemovedKey =  key.substring(CIPHER.length());
                String decodedValue = decode((String) rawValue);
                propertyOverrides.put(cipherRemovedKey, decodedValue);
            }
        }
    }
}

  public String decode(String encodedString) {
    byte[] valueDecoded = org.apache.commons.codec.binary.Base64.decodeBase64(encodedString);
    return new String(valueDecoded);
  }

Вот инициализатор загрузки Spring

@SpringBootApplication
@ComponentScan(basePackages = "com.amazonaws.serverless.sample.springboot.controller")
public class Application extends SpringBootServletInitializer {

    @Bean
    public HandlerMapping handlerMapping() {
        return new RequestMappingHandlerMapping();
    }

    @Bean
    public HandlerAdapter handlerAdapter() {
        return new RequestMappingHandlerAdapter();
    }

    @Bean
    public HandlerExceptionResolver handlerExceptionResolver() {
        return new HandlerExceptionResolver() {
            @Override
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
                return null;
            }
        };
    }

   //loading the initializer here
   public static void main(String[] args) {
     SpringApplication application=new SpringApplication(Application.class);
     application.addInitializers(new DecryptedPropertyContextInitializer());
     application.run(args);
    }

Это работает при запуске в качестве приложения весенней загрузки, но когда оно развернуто каклямбда в AWS метод main () в моем SpringBootServletInitializer никогда не будет вызываться лямбда.Вот мой обработчик Lambda.

public class StreamLambdaHandler implements RequestStreamHandler {
  private static Logger LOGGER = LoggerFactory.getLogger(StreamLambdaHandler.class); 

    private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    static {
        try {
            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
            handler.onStartup(servletContext -> {
                FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter", CognitoIdentityFilter.class);
                registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
            });
        } catch (ContainerInitializationException e) {
            e.printStackTrace();
            throw new RuntimeException("Could not initialize Spring Boot application", e);
        }
    }

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {
        handler.proxyStream(inputStream, outputStream, context);
        outputStream.close();
    }
}

Какие изменения необходимо внести в код для загрузки ApplicationContextInitializer от Lambda?Любая помощь будет высоко оценена.

1 Ответ

0 голосов
/ 30 января 2019

Я смог прибить его следующим образом.

Сначала изменил значение свойства с помощью заполнителя с префиксом, где префикс обозначает значения, которые необходимо расшифровать, например.

spring.datasource.password = $ {MY_PREFIX_placeHolder}

Имя переменной лямбда-окружения aws должно соответствовать заполнителю

('MY_PREFIX_placeHolder'), и его значение шифруется с использованием AWS KMS (этот примердекодирование base64).

создайте ApplicationContextInitializer, который будет расшифровывать значение свойства

public class DecryptedPropertyContextInitializer 
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {

  private static final String CIPHER = "MY_PREFIX_";

  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
      ConfigurableEnvironment environment = applicationContext.getEnvironment();
      for (PropertySource<?> propertySource : environment.getPropertySources()) {
          Map<String, Object> propertyOverrides = new LinkedHashMap<>();
          decodePasswords(propertySource, propertyOverrides);
          if (!propertyOverrides.isEmpty()) {
              PropertySource<?> decodedProperties = new MapPropertySource("decoded "+ propertySource.getName(), propertyOverrides);
              environment.getPropertySources().addBefore(propertySource.getName(), decodedProperties);
          }
      }
  }

  private void decodePasswords(PropertySource<?> source, Map<String, Object> propertyOverrides) {
    if (source instanceof EnumerablePropertySource) {
        EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) source;
        for (String key : enumerablePropertySource.getPropertyNames()) {
            Object rawValue = source.getProperty(key);
            if (rawValue instanceof String && key.startsWith(CIPHER)) {
                String decodedValue = decode((String) rawValue);
                propertyOverrides.put(key, decodedValue);
            }
        }
    }
}

  public String decode(String encodedString) {
    byte[] valueDecoded = org.apache.commons.codec.binary.Base64.decodeBase64(encodedString);
    return new String(valueDecoded);
  }
}

Приведенный выше код расшифрует все значения с префиксом MY_PREFIX_ и добавит их в начало источника свойства.

Поскольку весенняя загрузка развернута в aws lambda, lambda не будет вызывать функцию main (), поэтому, если ApplicationContextInitializer инициализируется в main (), он не будет работать.Чтобы заставить его работать, нужно переопределить метод createSpringApplicationBuilder () SpringBootServletInitializer, поэтому SpringBootServletInitializer будет выглядеть как

@SpringBootApplication
@ComponentScan(basePackages = "com.amazonaws.serverless.sample.springboot.controller")
public class Application extends SpringBootServletInitializer {

    @Bean
    public HandlerMapping handlerMapping() {
        return new RequestMappingHandlerMapping();
    }

    @Bean
    public HandlerAdapter handlerAdapter() {
        return new RequestMappingHandlerAdapter();
    }

    @Bean
    public HandlerExceptionResolver handlerExceptionResolver() {
        return new HandlerExceptionResolver() {
            @Override
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
                return null;
            }
        };
    }


@Override
protected SpringApplicationBuilder createSpringApplicationBuilder() {
  SpringApplicationBuilder builder = new SpringApplicationBuilder();
  builder.initializers(new DecryptedPropertyContextInitializer());
  return builder;
}

   public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
    }
}

Нет необходимости вносить какие-либо изменения для lambdahandler.

...