I have an application that has dependency where the security is defined. Typical config:
@Configuration
@EnableWebSecurity
public class AuthConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(DEFAULT_PERMIT_ALL_PATHS).permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.exceptionHandling()
.and()
.csrf().disable().httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
User.UserBuilder users = User.withDefaultPasswordEncoder();
authManagerBuilder.inMemoryAuthentication().withUser(users.username("FOO").password("BAR").roles("USER").build());
}
}
The above is the first project. And I have a second project where I have a POM dependency for this above. Now I would like to add an additional user, but I have to do this in my second project where there is just dependency to the first project with security.
I can copy&paste the whole AuthConfiguration, then I have 2 security filter chains but it is rather a bad idea. The app starts processing requests looking at what destination URI is. Two filter chains have the same antMatchers (I only want to add additional users) and only one filter chain is being processed. As a result, it works partially - user from the first or second project works (it depends on @Order of AuthConfiguration).
Maybe somehow it is possible to inject for instance AuthenticationManagerBuilder in my second project and extend/add additional users? I tried but it does not work. I added such code in the second project:
@Configuration
public class AdditionalUsersConfiguration() {
public AdditionalUsersConfiguration(AuthenticationManagerBuilder builder) throws Exception {
User.UserBuilder users = User.withDefaultPasswordEncoder();
authManagerBuilder.inMemoryAuthentication()
.withUser(users.username("FOO").password("BAR").roles("USER")
.withUser(users.username("FOO2").password("BAR").roles("USER").build());
}
}
Only first user FOO works, what looks like it is from the first project where it is defined.
Edit: I tried also approach from this question How can I add users to the inMemoryAuthentication builder after it has been built? I added this code in my second project:
@Configuration
public class NextApproachConfiguration() {
public NextApproachConfiguration(InMemoryUserDetailsManager manager) throws Exception {
manager.createUser(new User("FOO2", "BAR", new ArrayList<GrantedAuthority>()));
// in debugger here I see two users [FOO, FOO2]
}
}
It looks good. In debugger (comment in code) I see user from the first project and new user. But when I request endpoint with postman I see internally in debugger how it calls to InMemoryUserDetailsManager::loadByUsername() and this manager has different instance than where I added user two. And it only has one user from the first project. When I start app with debbuger I see that firstly is executed my above code where I add second user, and then is executed code from the first project (AuthConfiguration class) where AuthenticationManagerBuilder::inMemoryAuthentication goes to InMemoryUserDetailsManagerConfigurer where creates new instance of InMemoryUserDetailsManager using new keyword.
CodePudding user response:
Finally in my second project I just extended AuthConfiguration class from the first project in this way:
@Configuration
@Order(90)
public class SecondProjectConfiguration extends AuthConfiguration {
@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
super.configure(authManagerBuilder); // it creates FOO user
User.UserBuilder users = User.withDefaultPasswordEncoder();
authManagerBuilder.inMemoryAuthentication()
.withUser(users.username("FOO2").password("BAR").roles("USER").build());
}
}
and it works correctly. Order(90)
was needed also because AuthConfiguration has default Order 100, and it was stopping application with errors on start. Very simple approach but tried it as last.
CodePudding user response:
Another approach you can take is to use a custom bean (say UserFactory) to your configuration in the first project and use it to retrieve all the users and add to in-memory user manager
@Autowired(required = false)
UserFactory userFactory;
@Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
User.UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManagerConfigurer configurer = authManagerBuilder.inMemoryAuthentication();
configurer.withUser(users.username("FOO").password("BAR").roles("USER").build());
if (userFactory != null) {
for (UserDetails userDetails: userFactory.getUsers()) {
configurer.withUser(userDetails);
}
}
}
And in your second project, you define the bean and add the additional users:
@Bean
public UserFactory userFactory() {
UserFactory userFactory = new UserFactory();
User.UserBuilder user = User.withDefaultPasswordEncoder();
userFactory.addUser(user.username("FOO2").password("BAR").roles("USER").build());
return userFactory;
}
This might suit better if you have many default users and want to avoid re-typing them in the extended configuration class.