迁移到Spring Security 6沿着Swagger配置和自定义过滤器

q7solyqu  于 2023-04-11  发布在  Spring
关注(0)|答案(1)|浏览(275)

我正在尝试将Sping Boot 应用程序从2.x迁移到3.x。
在Sping Boot 2.x上,我有两个扩展WebSecurityConfigurerAdapter的配置,一个用于通过Swagger私下提供API文档,另一个用于常规API请求。
下面是两个预先存在的、工作良好的Spring Security配置文件。
通过“良好的工作”,我实现了以下三个要求。

  • 用户必须提供用户名和密码才能访问Swagger,地址为/swagger-ui/index.html
  • v1/auth/login不应该用JWTRequestFilter处理,因为用户在登录之前没有任何JWT。
  • 任何其他端点都应通过JWTRequestFilter进行保护。
// Configuration file for Swagger
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
class SwaggerConfig() : WebSecurityConfigurerAdapter() {
   
   // ommitted..

    override fun configure(http: HttpSecurity) {
        http.requestMatchers()
            .antMatchers("/swagger-ui/**", "/v3/api-docs/**")
            .and()
            .authorizeRequests()
            .anyRequest()
            .hasRole(SWAGGER_USER_ROLE)
            .and()
            .httpBasic()
    }

    @Bean
    fun passwordEncoder() = BCryptPasswordEncoder()

    @Bean
    @DependsOn("passwordEncoder")
    fun configureGlobal(auth: AuthenticationManagerBuilder): UserDetailsService {
        val user = User.builder()
            .passwordEncoder(passwordEncoder()::encode)
            .username(SWAGGER_USERNAME)
            .password(SWAGGER_PASSWORD)
            .roles(SWAGGER_USER_ROLE)
            .build()
        return InMemoryUserDetailsManager(user)
    }
}

// Configuration file for regular API calls.
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig(
    private val jwtVerifier: JwtVerifier,
    private val authenticationEntryPoint: AuthenticationEntryPoint
) : WebSecurityConfigurerAdapter() {

    override fun configure(http: HttpSecurity) {
        http {
            httpBasic {
                disable()
            }
            headers {
                frameOptions { disable() }
            }
            csrf { disable() }
            cors { configurationSource = corsConfigurationSource() }
            logout { disable() }
            sessionManagement {
                sessionCreationPolicy = SessionCreationPolicy.STATELESS
            }
            addFilterBefore(
                JWTRequestFilter(jwtVerifier, authenticationEntryPoint),
                UsernamePasswordAuthenticationFilter::class.java
            )
            addFilterAfter(
                RequestLoggingFilter(),
                JWTRequestFilter::class.java
            )
            exceptionHandling {
                authenticationEntryPoint
            }
        }
    }

    override fun configure(web: WebSecurity) {
        web.ignoring()
            .mvcMatchers(HttpMethod.POST, "/v1/auth/login")
    }
}

当我尝试迁移到使用Spring Security 6的Spring Boot 3.0.5时,我已经按照official Spring Security文档更改了我的代码,因为WebSecurityConfigurerAdapter已被弃用。
下面是我修改的代码。

// In SwaggerConfig
@Bean(value = ["swaggerConfigFilterChain"])
@Order(Ordered.HIGHEST_PRECEDENCE)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http
        .authorizeHttpRequests()
        .requestMatchers("/swagger-ui/**", "/v3/api-docs/**")
        .hasRole(SWAGGER_USER_ROLE)
        .and()
        .httpBasic()
    return http.build()
}

// In SecurityConfig
@Bean(value = ["securityConfigFilterChain"])
@Order(2)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http
        .httpBasic{ it.disable() }
        .headers { header -> header.frameOptions { it.disable() } }
        .csrf { it.disable() }
        .cors { it.configurationSource(corsConfigurationSource()) }
        .logout { it.disable() }
        .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
        .addFilterBefore(JWTRequestFilter(jwtVerifier, authenticationEntryPoint), UsernamePasswordAuthenticationFilter::class.java)
        .addFilterAfter(RequestLoggingFilter(), JWTRequestFilter::class.java)
        .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
    return http.build()
}

@Bean(value = ["securityConfigWebSecurityCustomizer"])
fun webSecurityCustomizer(): WebSecurityCustomizer {
    return WebSecurityCustomizer { web: WebSecurity ->
        web.ignoring()
        .requestMatchers(HttpMethod.POST, "/v1/auth/login")
    }
}

迁移到上面的代码后,Swagger仍然需要用户名、密码验证,这是我想要的,v1/auth/login没有通过JWTRequestFilter处理,这也是我想要的。
但是,其他路径没有被JWTRequestFilter处理。过滤器本身没有被调用,我得到的唯一响应是401(UNAUTHORIZED),响应体为空。
我错过了什么?

  • 如果我在SwaggerConfigSecurityConfig之间更改@OrderJWTRequestFilter将被正确调用,但它也会在Swagger端点上被调用。
xeufq47z

xeufq47z1#

TLDR;我应该使用securityMatcher()
在Spring Security中,我们可以配置API路径模式应该调用哪个SecurityFilterChain

我之前的代码的问题是,我在authorizeHttpRequests()中使用了requestMatchers(),而我应该像文档中描述的那样使用securityMatcher()来定义在调用特定API路径模式时应该调用哪个SecurityFilterChain
下面是我更新的代码。不管@Order注解如何,它都可以工作。

// In SwaggerConfig class.
@Bean(value = ["swaggerConfigFilterChain"])
@Order(2)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http
        .securityMatcher("/swagger-ui/**", "/v3/api-docs/**")
        .authorizeHttpRequests {
            it.requestMatchers(
                AntPathRequestMatcher("/swagger-ui/**", HttpMethod.GET.name()),
                AntPathRequestMatcher("/v3/api-docs/**", HttpMethod.GET.name()))
                .hasRole(SWAGGER_USER_ROLE)
                .and().httpBasic()
        }
    return http.build()
}

// In SecurityConfig class.
@Bean(value = ["securityConfigFilterChain"])
@Order(1)
fun filterChain(http: HttpSecurity):  DefaultSecurityFilterChain{
    http
        .httpBasic{ it.disable() }
        .headers { header -> header.frameOptions { it.disable() } }
        .csrf { it.disable() }
        .cors { it.configurationSource(corsConfigurationSource()) }
        .logout { it.disable() }
        .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
        .securityMatcher("/v1/**", "/v2/**")
        .authorizeHttpRequests {
            it.requestMatchers("/v1/**", "/v2/**").authenticated()
                .and()
                .apply(JWTRequestSecurityConfig(jwtVerifier, authenticationEntryPoint))
                .and()            .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
        }
    return http.build()
}

如果在@EnableWebSecurity中将debug设置为true,就像@EnableWebSecurity(debug = true)一样,您可以看到调用哪些过滤器来处理请求。
在应用securityMatcher()之前,调用的过滤器(过滤器链)是@Bean,配置了较低的@Order,而不管API路径模式如何。
然而,在应用securityMatcher()之后,调用来处理的过滤器因我定义的路径模式而不同。
例如,当我调用Swagger(GET /swagger-ui/index.html)时,会调用以下过滤器。

Security filter chain: [
  DisableEncodeUrlFilter
  WebAsyncManagerIntegrationFilter
  SecurityContextHolderFilter
  HeaderWriterFilter
  CsrfFilter
  LogoutFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  ExceptionTranslationFilter
  AuthorizationFilter
]

但是当我调用例如POST /v2/some-api,它满足模式.securityMatcher("/v1/**", "/v2/**")时,下面的过滤器被调用。

Security filter chain: [
  DisableEncodeUrlFilter
  WebAsyncManagerIntegrationFilter
  SecurityContextHolderFilter
  HeaderWriterFilter
  CorsFilter
  JWTRequestFilter
  RequestLoggingFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  AuthorizationFilter
]

相关问题