简体   繁体   中英

Spring profile default behaviour

I have a spring profile "DEV" and thats the only profile I have and I do NOT want to create a "production" profile. So only when profile is "DEV" I would like a certain type of bean for spring security is initiated (which is an in memory guest user and a userdetails bean)

But if no spring profile is provided in my tomcat startup, which is the case in production, I would like my app to continue what it is already doing(using ldap authenticatin provider).

Is there a way to define a "default" bean behaviour without actually needing to provide a profile at start up? Or you can look at my code below and suggest a different solution maybe.

@Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth, final AuthenticationProvider provider) throws Exception {

        auth
                .eraseCredentials(false)
                .authenticationProvider(provider)
                .authenticationProvider(jwtConfig.jwtAuthenticationProvider());
}


@Bean
public UserDetailsService userDetailsService() {
    final LdapUserDetailsService ldapUserDetailsService = new LdapUserDetailsService(ldapUserSearch(), ldapAuthoritiesPopulator());
    return new CompositeUserDetailsService(Arrays.asList(technicalUserDetailsService(), ldapUserDetailsService));
}

@Bean
@Profile("DEV")
public UserDetailsService devUserDetailsService() {
 useAnonymous = true;
        InMemoryUserDetailsManagerBuilder b = new InMemoryUserDetailsManagerBuilder()
                .withUser("user").password("password").authorities(ROLE_USER, ROLE_ADMIN).and();

        return new CompositeUserDetailsService(Arrays.asList(b.build(),
                technicalUserDetailsService()));

}
@Bean
public AuthenticationProvider ldapAuthenticationProvider() {
    final BindAuthenticator ba = new BindAuthenticator((BaseLdapPathContextSource) contextSource());
    ba.setUserSearch(ldapUserSearch());
    return new LdapAuthenticationProvider(ba, ldapAuthoritiesPopulator());
}

I think there is a misunderstanding of what @Profile does. Beans marked with @Profile are only loaded when that profile is active, but all other beans (without a @Profile ) are still always loaded, regardless of the chosen profile.

I see a few ways to solve this:

1) Mark all those beans with a @Profile("dev") also with @Primary so Spring knows which one to pick when two beans of the same type are loaded (since you donot want to use a production profile).

2) Mark the beans that should not be loaded when profile dev is active with @Profile("!dev") -- only applicable for Spring 3.2 and higher (see https://github.com/spring-projects/spring-framework/commit/bcd44f3798ed06c0704d2a3564b8a9735e747e87 ).

Or...

3) Use a production profile and simply activate it in for example the web.xml file (something which you probably don't use locally).

Simply create multiple @Configuration classes and mark the entire class with a profile (it also helps to keep related stuff together). A typical example is for the database. Create one configuration class for the production database (something with JNDI and Oracle) and one for local development and testing (HSQLDB).

You mark the JNDI Configuration class with @Profile("production") and the other with @Profile("dev") -- no need to mark individual beans, just split them logically amongst two different @Configuration classes.

This worked really well for us, also when combined with integration testing.

I would override the bean definition in this way:

@Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth, final AuthenticationProvider provider) throws Exception {

        auth
                .eraseCredentials(false)
                .authenticationProvider(provider)
                .authenticationProvider(jwtConfig.jwtAuthenticationProvider());
}


@Bean("myUserDetailService")
public UserDetailsService userDetailsService() {
    final LdapUserDetailsService ldapUserDetailsService = new LdapUserDetailsService(ldapUserSearch(), ldapAuthoritiesPopulator());
    return new CompositeUserDetailsService(Arrays.asList(technicalUserDetailsService(), ldapUserDetailsService));
}

@Bean("myUserDetailService")
@Profile("DEV")
public UserDetailsService devUserDetailsService() {
 useAnonymous = true;
        InMemoryUserDetailsManagerBuilder b = new InMemoryUserDetailsManagerBuilder()
                .withUser("guest").password("guest").authorities(ROLE_USER, ROLE_ADMIN).and();

        return new CompositeUserDetailsService(Arrays.asList(b.build(),
                technicalUserDetailsService()));

}
@Bean("myAuthProvider")
public AuthenticationProvider ldapAuthenticationProvider() {
    final BindAuthenticator ba = new BindAuthenticator((BaseLdapPathContextSource) contextSource());
    ba.setUserSearch(ldapUserSearch());
    return new LdapAuthenticationProvider(ba, ldapAuthoritiesPopulator());
}

@Bean("myAuthProvider")
@Profile("DEV")
public AuthenticationProvider devAuthenticationProvider() {

   //find a way to return userdetails here

}

In this way when the "DEV" profile is started the beans defined in that profile should override the default beans

Sure when you autowire beans you should use the @Qualifier annotation

I hope it's useful

Angelo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM