Spring - różne sposoby uwierzytelnienia na różne endpointy

0

Potrzebuję zaimplementować jednocześnie JWT i Basic auth. JWT obsługuje właściwie całą aplikację poza jednym endpointem (np /basicauth/*), który jest obsługiwany przez Basic Auth.
Do JWT używam OncePerRequestFilter

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private Authenticator authenticatior;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                .and()
                .addFilterAfter(
                        new AuthenticationFilter(authenticatior),
                        UsernamePasswordAuthenticationFilter.class
                )
                .authorizeRequests()
                .anyRequest()
                .authenticated();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers(HttpMethod.GET, "/jwt")
                .antMatchers(HttpMethod.OPTIONS, "/**")
                .regexMatchers(
                        HttpMethod.POST,
                        "/jwt"
                );
    }
}

i działa perfect. Odłączam /jwt od wymaganego tokenu, pozostałe endpointy wymagają nagłówka Authorization: Bearer w żądaniu.

Niestety nie udaje mi się skonfigurować security tak, aby endpoitn /basicauth/* był obsługiwany przez Basic Auth, nie przez JWT.

To co mam do teraz:

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private Authenticator authenticatior;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("GUEST");
    }

    @Bean
    public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
        return new CustomBasicAuthenticationEntryPoint();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf()
                .disable()

                .authorizeRequests()
                .antMatchers("/basicauth/**")
                .hasRole("GUEST")
                .and()
                .httpBasic()
                .realmName("BASICAUTHREALM")
                .authenticationEntryPoint(getBasicAuthEntryPoint())
                .and()


                .antMatcher("/**")
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                .and()
                .addFilterAfter(
                        new AuthenticationFilter(authenticatior),
                        UsernamePasswordAuthenticationFilter.class
                )

                .authorizeRequests()
                .anyRequest()
                .authenticated();
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers(HttpMethod.GET, "/jwt")
                .antMatchers(HttpMethod.OPTIONS, "/**")
                .regexMatchers(
                        HttpMethod.POST,
                        "/jwt"
                );
    }
}

CustomBasicAuthenticationEntryPoint.java:

public final class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {

    @Override
    public void commence(final HttpServletRequest request,
                         final HttpServletResponse response,
                         final AuthenticationException authException) throws IOException {
        //Authentication failed, send error response.
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");

        PrintWriter writer = response.getWriter();
        writer.println("HTTP Status 401 : " + authException.getMessage());
    }

    @Override
    public void afterPropertiesSet() {
        setRealmName("MY_TEST_REALM");
        super.afterPropertiesSet();
    }
}

Taka konfiguracja zwraca 401 na każde żądanie /basicauth/* i nie pyta o basic auth credentials.

Jak prawidłowo skonfigurować SecurityConfiguration aby utrzymać jwt na istniejących endpointach i jednocześnie zabezpieczyć basicauth na /basicauth/*?

1
  1. W zasadzie czemu tak robisz że masz osobny endpoint? Czemu nie dać po prostu różnych authów na ten sam endpoint? Akurat basica z tokenem można spokojnie spiąć:
@Configuration
public class SomeConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher(SOME_PATH + "/**")
                .authorizeRequests(authorizeRequests -> authorizeRequests
                        .antMatchers(SOME_PATH + "/**")
                        .authenticated()
                )
                .httpBasic(basic -> basic.authenticationEntryPoint(authenticationEntryPoint))
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .cors();
    }
}
  1. Ale jak bardzo chcesz mieć osobne to też nie widzę problemu, ja bym zrobił jakieś:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Inject
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(basicAuthenticationProvider); // czy coś tam
    }

    @Configuration
    @Order(1)
    public static class BasicSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher(BASIC_PATH + "/**")
                    .httpBasic(SecurityConfigurerAdapter::and)
                    .authorizeRequests(authorizeRequests -> authorizeRequests
                            .antMatchers(BASIC_PATH + "/**").authenticated()
                    )
                    .cors();
        }
    }

    @Configuration
    @Order(2)
    public static class TokenSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher(TOKEN_PATH + "/**")
                    .authorizeRequests(authorizeRequest -> authorizeRequest
                            .antMatchers(TOKEN_PATH + "/**").authenticated())
                    .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                    .cors();
        }
    }
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1