Spring Boot中的Spring Security 3

28

我目前正在将我们的REST应用程序从Spring Boot 2.7.5迁移到3.0.0-RC2。 我希望除了Open API URL之外,其他所有内容都是安全的。 在Spring Boot 2.7.5中,我们曾经这样做:

@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();
  }
}

并且它运行良好。在Spring Boot 3中,我不得不将其更改为

@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();
  }
}

由于WebSecurityConfigurerAdapter已被移除,代码不再起作用。开放式API URL也通过基本身份验证进行了保护。在升级代码时,我是否犯了错误或者这可能是Spring Boot 3 RC 2的问题?

更新 由于大多数新API已经在2.7.5中可用,因此我已经在我们的2.7.5代码库中将代码更新为以下内容:

@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();
  }
}

在我们的3.0.0-RC2分支中,代码现在如下:

@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();
  }
}

正如您所看到的,唯一的区别在于我调用了requestMatchers而不是antMatchers。这种方法似乎已被重命名。方法antMatchers不再可用。不过最终效果仍然相同。在我们3.0.0-RC2分支上,Spring Boot要求对OpenAPI URL进行基本身份验证。在2.7.5上仍然可以正常工作。


我应该提一下我正在使用Jersey。也许这与此有关? - Thomas Oellrich
你是否实际上有一个"/openapi/openapi.yml"的处理程序(控制器映射)?如果没有处理程序,它将被解析为不是404 NOT_FOUND。这反过来会重定向到/error。由于/error也受到保护,因此它会要求您登录。 - Elyorbek Ibrokhimov
是的,我有。一旦我输入基本身份验证的凭据,就会显示Open API。 - Thomas Oellrich
2
更新:您必须使用新版本的Springdoc。https://springdoc.org/。请注意文本*对于spring-boot v3支持,请确保使用springdoc-openapi v2*。 - Raphaël Colantonio
7个回答

21
作者: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();
  }

建议您立即使用Spring Boot 3.0.0(GA)版本,而不是RC版本。

7

在我的WebSecurityConfig中,我做了这个:

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();
}

使用Dockerfile(执行mvn clean package并从Docker运行.jar文件)可以帮助您避免在Swagger UI内部身份验证方面出现问题。希望这些内容对您有所帮助 :)

3
第一种方法和第二种方法有什么区别?除了方法和参数名称之外,它们看起来是相同的。您能否将第二个bean中的指令链接到第一个bean中并完全删除第二个bean? - Marco Lackovic
第二个使用了已弃用的httpBasic()方法。第一个添加了一个过滤器,我用它来验证请求并设置httpBasics withDefaults()。这样可以添加默认行为,就像Panagiotis Bougioukos在这里的回答中所说的那样:https://stackoverflow.com/questions/73529846/what-is-the-purpose-of-withdefaults-is-spring-security 在我写这个答案的时候,我尝试了很多不同的方法来使它工作,但都没有成功。这个配置是我唯一成功的一个。 - undefined

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();
    }

它有效。

@banan3'14 你编辑了什么?是在一行中的空格还是一个制表符? - chris_yooo
代码缩进现在更好了,而且没有像ot这样的单词 - 它被替换了。即使是外观上的改变在stackoverflow上也是受欢迎的,只要它们能改善内容 - 你可以在这个答案中阅读更多关于这种方法的信息:https://meta.stackoverflow.com/a/278091/7769052 - banan3'14

2

使用

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

为端点指定身份验证。

      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.

在您的情况下,您可以定义两个安全过滤器链:一个用于公共端点,另一个用于安全端点。并为它们指定适当的顺序:

    @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();
      }

这似乎过于复杂了。我采用了安迪·威尔金森的建议(请参见詹姆斯·格雷的答案)。 - Thomas Oellrich
这段代码也会导致错误,因为不能两次提供相同的方法名“configure”。 - nonNumericalFloat

2
我的安全配置如下:
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();

我使用的是3.1.1版本,我看到一个编译时错误。.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);因为UsernamePasswordAuthentication仍然使用javax servlet,而OnceperRequestfilet是Jakarta。 - undefined
@Jayanth - 对我来说有效:org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - undefined

1

官方文档提供了一个示例,我在这里使用您的配置进行了概述:

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

return http.build();

你可以尝试这个方法,因为它会将“请求”更改为“交换”措辞,以配合声明式客户端的迁移(@PostExchange vs. @PostMapping)。希望能有所帮助。

1
不行,那样做不起作用。HttpSecurity没有authorizeExchange方法。你的例子是针对我没有使用的WebFlux的: https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.html - Thomas Oellrich

-3

这似乎是Spring Boot 3中的一个bug。我已经提出了问题


我认为不是这样的,请检查HttpSecurity中的securityMatcher()方法。 - Sharofiddin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接