How to autowire Spring Beans into Lamba code?

Viewed 52

I'm setting up a spring-security saml 2.0 blueprint for my company.

I'm following the Howtos of Ulises Bocchio. Everything is fine so far - the sample authentication process works fine.

To follow the security guideline of our company, I have to make the SAMLConfig.java file configurable. My first thought was to autowire a component that loads the configuration from "somewhere"; like the following.

/**
* @author Ulises Bocchio
*/
@AutoConfigureBefore(WebSecurityConfig.class)
@Configuration
public class SAMLConfig {
  protected Logger log = LoggerFactory.getLogger(this.getClass());

  @Autowired
  private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;


  @Autowired
  private SAMLProperties samlProperties;

  @Bean
  public SAMLAuthenticationProvider samlAuthenticationProvider() {
    final SAMLAuthenticationProvider provider = new SAMLAuthenticationProvider();
    provider.setUserDetails(this.samlUserDetailsServiceImpl);
    provider.setForcePrincipalAsString(false);
    return provider;
  }

This won't work, because the SAMLProperties object is null. I'm not sure but is it possible, that spring security is initiated before other (application) components?

How can I achieve a dynamic configuration of Ulises Bocchio's SAMLConfig.java file?

1 Answers

I was hinted by a colleague to the following INFO-log-line:

@Bean method SAMLConfig.idpMetadataLoader is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.

My adapted version of the idpMetadataLoader method by Ulises Bocchio:

@Bean
BeanFactoryPostProcessor idpMetadataLoader(StaticBasicParserPool parserPool) {
return beanFactory -> {
  try {
    final Resource idpResource = new UrlResource(this.samlProperties.getIdpMetadataUrl());
    final String alias = this.samlProperties.getKeystoreAlias();
    final Timer refreshTimer = new Timer(true);

    final ResourceBackedMetadataProvider delegate =
        new ResourceBackedMetadataProvider(refreshTimer, new SpringResourceWrapperOpenSAMLResource(idpResource));
    delegate.setParserPool(parserPool);
    final ExtendedMetadata extendedMetadata = this.extendedMetadata().clone();
    final ExtendedMetadataDelegate provider = new ExtendedMetadataDelegate(delegate, extendedMetadata);
    provider.setMetadataTrustCheck(true);
    provider.setMetadataRequireSignature(false);
    extendedMetadata.setAlias(alias);
    beanFactory.registerSingleton(alias, provider);
  } catch (final Exception e) {
    this.log.error("Error while confiure SAML", e);
    e.printStackTrace();
  }
 };

}

The point is, that the lambda code is translated in static code - so non-static class members can't be accessed. Quite simple solution was to deliver the Configuration as a method parameter.

  @Bean
  BeanFactoryPostProcessor idpMetadataLoader(SAMLConfigFile properties, StaticBasicParserPool parserPool) {
    return beanFactory -> {
  ...
  };
}
Related