19

I'm currently in the process of migrating our REST application from Spring Boot 2.7.5 to 3.0.0-RC2. I want everything to be secure apart from the Open API URL. In Spring Boot 2.7.5, we used to do this:

@Named
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/openapi/openapi.yml").permitAll()
        .anyRequest().authenticated()
        .and()
        .httpBasic();
  }
}

and it worked fine. In Spring Boot 3, I had to change it to

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((requests) -> requests
            .requestMatchers("/openapi/openapi.yml").permitAll()
            .anyRequest()
            .authenticated())
        .httpBasic();

    return http.build();
  }
}

since WebSecurityConfigurerAdapter has been removed. It's not working though. The Open API URL is also secured via basic authentication. Have I made a mistake when upgrading the code or is that possibly an issue in Spring Boot 3 RC 2?

Update Since most of the new API was already available in 2.7.5, I've updated our code in our 2.7.5 code base to the following:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests((requests) -> requests
            .antMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
            .antMatchers("/openapi/openapi.yml").permitAll()
            .anyRequest().authenticated())
        .httpBasic();
    return http.build();
  }
}

In our branch for 3.0.0-RC2, the code is now as follows:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests((requests) -> requests
            .requestMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
            .requestMatchers("/openapi/openapi.yml").permitAll()
            .anyRequest().authenticated())
        .httpBasic();
    return http.build();
  }
}

As you can see, the only difference is that I call requestMatchers instead of antMatchers. This method seems to have been renamed. The method antMatchers is no longer available. The end effect is still the same though. On our branch for 3.0.0-RC2, Spring Boot asks for basic authentication for the OpenAPI URL. Still works fine on 2.7.5.

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
Thomas Oellrich
  • 1,019
  • 2
  • 8
  • 13
  • I should probably mention that I'm using Jersey. Maybe that has something to do with it? – Thomas Oellrich Nov 15 '22 at 18:00
  • Do you actually have a handler(controller mapping)for `"/openapi/openapi.yml"`? If there is no handler, it is resolved to not `404 NOT_FOUND`. Which in turn redirects to `/error`. Since `/error` is also secured it will ask you for login. – Elyorbek Ibrokhimov Nov 16 '22 at 02:28
  • Yes, I do. Once I enter the credentials of basic authentication, the Open API is shown. – Thomas Oellrich Nov 16 '22 at 05:55
  • 1
    Update: You *must* use new version of Springdoc. https://springdoc.org/ . Focus at text *For spring-boot v3 support, make sure you use springdoc-openapi v2*. – Vy Do Jan 10 '23 at 01:18

7 Answers7

18

Author: https://github.com/wilkinsona

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(requests -> requests
            .requestMatchers(new AntPathRequestMatcher("/openapi/openapi.yml")).permitAll()
            .anyRequest().authenticated())
        .httpBasic();
    return http.build();
  }

Source: https://github.com/spring-projects/spring-boot/issues/33357#issuecomment-1327301183

I recommend you use Spring Boot 3.0.0 (GA) right now, not RC version.

Michael
  • 41,989
  • 11
  • 82
  • 128
Vy Do
  • 46,709
  • 59
  • 215
  • 313
7

Inside my WebSecurityConfig, I did this:

private static final String[] AUTH_WHITELIST = {
        // -- Swagger UI v2
        "/v2/api-docs",
        "v2/api-docs",
        "/swagger-resources",
        "swagger-resources",
        "/swagger-resources/**",
        "swagger-resources/**",
        "/configuration/ui",
        "configuration/ui",
        "/configuration/security",
        "configuration/security",
        "/swagger-ui.html",
        "swagger-ui.html",
        "webjars/**",
        // -- Swagger UI v3
        "/v3/api-docs/**",
        "v3/api-docs/**",
        "/swagger-ui/**",
        "swagger-ui/**",
        // CSA Controllers
        "/csa/api/token",
        // Actuators
        "/actuator/**",
        "/health/**"
};

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests( auth -> auth
                    .requestMatchers(AUTH_WHITELIST).permitAll()
                    .anyRequest().authenticated()
            )
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .httpBasic(withDefaults())
            .addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
            //.addFilterAfter(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
            .build();
}

@Bean
public SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .authorizeHttpRequests((requests) -> requests
                    .requestMatchers( new AntPathRequestMatcher("swagger-ui/**")).permitAll()
                    .requestMatchers( new AntPathRequestMatcher("/swagger-ui/**")).permitAll()
                    .requestMatchers( new AntPathRequestMatcher("v3/api-docs/**")).permitAll()
                    .requestMatchers( new AntPathRequestMatcher("/v3/api-docs/**")).permitAll()
                    .anyRequest().authenticated())
            .httpBasic();
    return httpSecurity.build();
}

This and using Dockerfile (doing mvn clean package and running .jar from Docker) made me had no issues with authentication inside swagger ui.

Hope this can help you :)

G. Ciardini
  • 1,210
  • 1
  • 17
  • 32
  • 3
    What is the difference between the first and second method? They look the same, method and parameter names excluded. Could you not chain the instructions in the second bean into the first bean and delete the second bean altogether? – Marco Lackovic Feb 08 '23 at 15:46
4

My security cfg looks like:

Spring 3.0.0

@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests(requests -> requests
                .requestMatchers(HttpMethod.GET, "/", "/static/**", "/index.html", "/api/users/me").permitAll()
                .requestMatchers(HttpMethod.POST, "/api/users").permitAll()
                .requestMatchers(HttpMethod.GET, "/api/users/login", "/api/users/{username}", "/api/users/logout", "/api/customers", "/api/storages").authenticated()
                .requestMatchers(HttpMethod.POST, "/api/customers", "/api/storages").authenticated()
                .requestMatchers(HttpMethod.PUT, "/api/customers/{id}", "/api/storages/{id}").authenticated()
                .requestMatchers(HttpMethod.DELETE, "/api/users/{id}", "/api/storages/{id}", "/api/customers/{id}").authenticated()
                .anyRequest().denyAll())
        .httpBasic();
    return http.build();
}

it works

banan3'14
  • 3,810
  • 3
  • 24
  • 47
chris_yooo
  • 41
  • 5
  • @banan3'14 you edited what ? the space in a line - 1 tab ? – chris_yooo Aug 02 '23 at 21:18
  • code indentation is now better and there's no word like *ot* – it instead. Even cosmetic changes are welcome on stackoverflow, as long as they improve the content – you can read more about that approach for example in this answer https://meta.stackoverflow.com/a/278091/7769052 – banan3'14 Aug 07 '23 at 10:11
2

Use

  http.securityMatcher("<patterns>")...

to specify authentication for endpoints.

      authorizeHttpRequests((requests) -> requests
                .requestMatchers("<pattern>")

only works for authorization, if you don't set securityMatcher , SecurityFilterChain by default gets any request for authentication. And any request will be authenticated by an authentication provider.

In your case, you can define two security filter, chains: one for public endpoitns, another for secured. And give them proper order:

    @Bean
    @Order(1)
    public SecurityFilterChain configurePublicEndpoints(HttpSecurity http) throws Exception {
        http.securityMatcher(OPTIONS,"/openapi/openapi.yml").csrf().disable()
            .authorizeHttpRequests((requests) -> requests
                .anyRequest().permitAll() // allow CORS option calls for Swagger UI
    );
        return http.build();
      }
    
    @Bean
    Order(2)
      public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http.securityMatcher("/**")
            .csrf().disable()
            .authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
            .httpBasic();
        return http.build();
      }
Sharofiddin
  • 340
  • 2
  • 14
1

The official documentation suggests an example which I have abridged here with your config:

http
  .authorizeExchange((exchanges) ->
    exchanges
      .pathMatchers("/openapi/openapi.yml").permitAll()
      .anyExchange().authenticated())
    .httpBasic();

return http.build();

You could try this, since it changes the "request" for the "exchange" wording, in line with the migration to declarative clients (@PostExchange vs. @PostMapping) I suppose. Hope it helps.

  • 1
    Nope, that doesn't work. HttpSecurity doesn't have a method authorizeExchange. Your example is for WebFlux which I'm not using: https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.html – Thomas Oellrich Nov 15 '22 at 17:59
1

My security config looks like:

Spring 3.1.1

   http.csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable())
                .authorizeHttpRequests((requests) -> requests
                        .requestMatchers("/swagger-ui/**").permitAll()
                        .anyRequest().authenticated())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable())) //to make accessible h2 console, it works as frame
                .exceptionHandling(httpSecurityExceptionHandlingConfigurer -> httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
                .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
Axel
  • 21
  • 2
-2

This seems to be a bug in Spring Boot 3. I've raised an issue.

Thomas Oellrich
  • 1,019
  • 2
  • 8
  • 13