springspring-bootspring-securitybasic-authentication

Multiple user details services for different endpoints


I am building a REST API using Spring and am currently authenticating all my requests using a custom user details service and this configuration code:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}

I am also setting up a DaoAuthenticationProvider to use the my user details service and using that to configure global security.

Now, I want to provide an endpoint that (while still secured with HTTP basic authentication) uses a different user details service to check whether the user is allowed to access the given resource.

How do I use two different user details services for different endpoints?


Solution

  • One thing you can do is have two WebSecurityConfigurerAdapters:

    @EnableWebSecurity
    @Order(Ordered.HIGHEST_PRECEDENCE)
    class FirstEndpointConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) {
            http
                .requestMatchers()
                    .antMatchers("/specialendpoint")
                    .and()
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .httpBasic();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) {
            auth.userDetailsService(/* first of your userDetailsServices */);
        }
    }
    
    
    @Configuration
    class SecondEndpointConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) {
            http // all other requests handled here
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .httpBasic();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) {
            auth.userDetailsService(/* second of your userDetailsServices */);
        }
    }
    

    requestMatchers() exists for targeting springSecurityFilterChains to specific endpoints.

    EDIT: Mahmoud Odeh makes a good point that if the user bases are the same, then you may not need multiple UserDetailsService instances. Instead, you can use one change that isolates your special endpoint by an authority on the user's account:

    http
        .authorizeRequests()
            .antMatchers("/specialendpoint").hasAuthority("SPECIAL")
            .anyRequest().authenticated()
            .and()
        .httpBasic();
    

    Then, your single UserDetailsService would look up all users. It would include the SPECIAL GrantedAuthority in the UserDetails instance for users who have access to /specialendpoint.