Summary

Based on that docs it's possible to configure two authentication methods at the same Spring Instance, but for some reason I can't not achieve this. I'm trying to use basic and form login.

Actual Behavior

One of the two auth methods will be ignored

curl -I localhost:8080/website && curl -I localhost:8080/api
HTTP/1.1 404 
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 27 Jul 2018 03:01:00 GMT

HTTP/1.1 401 
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=99E8D742F1E96361D9430B4BC09D55A5;path=/;HttpOnly
WWW-Authenticate: Basic realm="Realm"
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 27 Jul 2018 03:01:00 GMT

Expected Behavior

  • /api/** protected by basic auth
  • /website/** protected by form login

Configuration / Version / Sample

I have a running sample right here

Comment From: mageddo

Okay, I got it.

  1. You have run the more specific configurations first
  2. You have to use antMatcher and not antMatchers

It works

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
        final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(
            User
                .withUsername("admin")
                .password(encoder.encode("admin"))
                .roles("ADMIN")
                .build()
        );
        return manager;
    }

    @Bean PasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }


    @Configuration
    @Order(1)
    public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/api/**")
                .authorizeRequests()
                    .anyRequest().hasRole("API_USER")
                .and();
        }
    }

    @Configuration
    @Order(2)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/website/**").hasRole("ADMIN")
                .and()
                .formLogin()
                .and()
                .logout().permitAll()
            ;
        }
    }
}

It doesn't

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
        final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(
            User
                .withUsername("admin")
                .password(encoder.encode("admin"))
                .roles("ADMIN")
                .build()
        );
        return manager;
    }

    @Bean PasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }

    @Configuration
    @Order(2)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/website/**").hasRole("ADMIN")
                .and()
                .formLogin()
                .and()
                .logout().permitAll()

            ;
        }
    }

    @Configuration
    @Order(1)
    public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                                .authorizeRequests()
                .antMatchers("/api/**").hasRole("API_USER")
                .and()
                .httpBasic();
        }

    }
}

Jeez, I lost 3 days working on that thing.

Comment From: chiragsoni2401

I am using above multiple configurations but at the place of form login I am redirecting to an okta idp it is working fine but at the place of httpBasic() if I you something else like fomLogin or if I not use anything means I directly make post request to /api/** then I am getting 403 forbidden error.

So basically I want to know that in case of multiple http elements it is necessary to have formLogin(), and httpBasics() or we can replace these as per our need.

Comment From: mageddo

I have this running example for form and basic http if it would help you

Comment From: pradeepkl

  • Any specific reason why we need to add the antMatcher instead of antMatchers?
  • Is the antMatcher only required for the Configuration marked with Order(1) ?