我正在尝试将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),响应体为空。
我错过了什么?
- 如果我在
SwaggerConfig
和SecurityConfig
之间更改@Order
,JWTRequestFilter
将被正确调用,但它也会在Swagger端点上被调用。
1条答案
按热度按时间xeufq47z1#
TLDR;我应该使用
securityMatcher()
。在Spring Security中,我们可以配置API路径模式应该调用哪个
SecurityFilterChain
。我之前的代码的问题是,我在
authorizeHttpRequests()
中使用了requestMatchers()
,而我应该像文档中描述的那样使用securityMatcher()
来定义在调用特定API路径模式时应该调用哪个SecurityFilterChain
。下面是我更新的代码。不管
@Order
注解如何,它都可以工作。如果在
@EnableWebSecurity
中将debug设置为true,就像@EnableWebSecurity(debug = true)
一样,您可以看到调用哪些过滤器来处理请求。在应用
securityMatcher()
之前,调用的过滤器(过滤器链)是@Bean
,配置了较低的@Order
,而不管API路径模式如何。然而,在应用
securityMatcher()
之后,调用来处理的过滤器因我定义的路径模式而不同。例如,当我调用Swagger(
GET /swagger-ui/index.html
)时,会调用以下过滤器。但是当我调用例如
POST /v2/some-api
,它满足模式.securityMatcher("/v1/**", "/v2/**")
时,下面的过滤器被调用。