忽略 Spring Boot 中特定 Url 的承载令牌验证
Posted
技术标签:
【中文标题】忽略 Spring Boot 中特定 Url 的承载令牌验证【英文标题】:Ignore Bearer Token Validation for specific Urls in Spring Boot 【发布时间】:2021-04-21 11:55:15 【问题描述】:我正在将微服务配置为资源服务器,它使用 JWK 端点来验证 JWT 令牌的签名。
我已将配置设置为允许服务中的所有GET
请求。所有其他请求都根据范围和角色进行保护。这是我正在使用的配置。
@EnableReactiveMethodSecurity
class SecurityConfig : WebFluxConfigurer
@Bean
fun authenticationEntryPoint(): ServerAuthenticationEntryPoint
return JwtBearerTokenServerAuthenticationEntryPoint()
@Bean
fun accessDeniedHandler(): ServerAccessDeniedHandler
return JwtTokenAccessDeniedHandler()
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
http
.authorizeExchange()
.pathMatchers(HttpMethod.GET).permitAll()
.pathMatchers("/docs/**", "/v2/api-docs/**", "/").permitAll()
// Client should have the required scope to write to products
.pathMatchers(HttpMethod.POST).hasAuthority(PRODUCT_WRITE_SCOPE)
.pathMatchers(HttpMethod.PUT).hasAuthority(PRODUCT_WRITE_SCOPE)
.pathMatchers(HttpMethod.DELETE).hasAuthority(PRODUCT_WRITE_SCOPE)
// health and info urls will be open(permitted to all) others will be checked for authorization
.matchers(EndpointRequest.to(HealthEndpoint::class.java, InfoEndpoint::class.java)).permitAll()
.anyExchange().authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.oauth2ResourceServer()
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler())
.jwt()
.jwtAuthenticationConverter
jwtAuthenticationConverter(it)
return http.build()
private fun jwtAuthenticationConverter(jwt: Jwt): Mono<AbstractAuthenticationToken>?
val jwtAuthConverter = ReactiveJwtAuthenticationConverter()
jwtAuthConverter.setJwtGrantedAuthoritiesConverter
val jwtGrantedAuthoritiesConverter = JwtAuthoritiesConverter()
val reactiveJwtGrantedAuthoritiesConverterAdapter =
ReactiveJwtGrantedAuthoritiesConverterAdapter(jwtGrantedAuthoritiesConverter)
reactiveJwtGrantedAuthoritiesConverterAdapter.convert(it)
return jwtAuthConverter.convert(jwt)
companion object
private const val PRODUCT_WRITE_SCOPE = "SCOPE_product:write"
我面临的问题是,如果我在 GET
请求的授权标头中发送过期令牌,令牌验证仍然会发生并且我收到令牌过期错误。
有没有办法改变配置,使令牌验证只发生在某些端点上,而忽略其他端点?
【问题讨论】:
为什么不在所有 get 请求中发送授权标头? 我在所有请求中发送授权标头。这就是问题出现的地方。如果令牌已过期,则 GET 请求失败,说明令牌已过期。 是的,我确实理解您的问题,但正如我所说,当您不想获得授权时,更好的解决方案是不发送令牌。 @Toerktumlare 这当然是一个选项,但我认为它可能会在客户端实现中添加更多条件检查,因此希望避免这种情况。如果没有办法做到这一点,那就是我会去的。 从语义上考虑,如果您发送Authorization
标头,您是在告诉后端“嗨,这是我的请求,这是证明我是谁的标头”。现在您想发送一个身份验证标头,即使您不想要或不应该,而是忽略它?对我来说,这听起来像是给自己制造更多问题。我在春天看不到任何明显的东西可以让你做到这一点,而且很确定春天同意我的看法。如果您不想被授权,请不要发送授权标头。
【参考方案1】:
这是我为解决问题所做的。您可以选择指定安全配置应应用于哪些路径。这是指定它的代码 sn-p。
.securityMatcher
ServerWebExchangeMatchers.matchers(
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/**"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.PUT, "/**"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.DELETE, "/**")
).matches(it)
这是完整的配置。
@EnableReactiveMethodSecurity
class SecurityConfig : WebFluxConfigurer
@Bean
fun authenticationEntryPoint(): ServerAuthenticationEntryPoint
return JwtBearerTokenServerAuthenticationEntryPoint()
@Bean
fun accessDeniedHandler(): ServerAccessDeniedHandler
return JwtTokenAccessDeniedHandler()
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain
http
.securityMatcher
ServerWebExchangeMatchers.matchers(
ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/**"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.PUT, "/**"),
ServerWebExchangeMatchers.pathMatchers(HttpMethod.DELETE, "/**")
).matches(it)
.authorizeExchange()
.pathMatchers("/docs/**", "/v2/api-docs/**", "/").permitAll()
// Client should have the required scope to write to products
.pathMatchers(HttpMethod.POST).hasAuthority(PRODUCT_WRITE_SCOPE)
.pathMatchers(HttpMethod.PUT).hasAuthority(PRODUCT_WRITE_SCOPE)
.pathMatchers(HttpMethod.DELETE).hasAuthority(PRODUCT_WRITE_SCOPE)
// health and info urls will be open(permitted to all) others will be checked for authorization
.matchers(EndpointRequest.to(HealthEndpoint::class.java, InfoEndpoint::class.java)).permitAll()
.anyExchange().authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.oauth2ResourceServer()
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler())
.jwt()
.jwtAuthenticationConverter
jwtAuthenticationConverter(it)
return http.build()
private fun jwtAuthenticationConverter(jwt: Jwt): Mono<AbstractAuthenticationToken>?
val jwtAuthConverter = ReactiveJwtAuthenticationConverter()
jwtAuthConverter.setJwtGrantedAuthoritiesConverter
val jwtGrantedAuthoritiesConverter = JwtAuthoritiesConverter()
val reactiveJwtGrantedAuthoritiesConverterAdapter =
ReactiveJwtGrantedAuthoritiesConverterAdapter(jwtGrantedAuthoritiesConverter)
reactiveJwtGrantedAuthoritiesConverterAdapter.convert(it)
return jwtAuthConverter.convert(jwt)
companion object
private const val PRODUCT_WRITE_SCOPE = "SCOPE_product:write"
【讨论】:
这种配置的缺点是你需要opt-in
来保证安全,而安全应该总是opt-out
。以上是关于忽略 Spring Boot 中特定 Url 的承载令牌验证的主要内容,如果未能解决你的问题,请参考以下文章
在 spring-boot 中禁用特定 url 的 Keycloak 身份验证
在aws上忽略基于url到spring boot 2应用程序的黑客攻击是不是安全?
特定 url 的多个身份验证提供程序 - Spring Boot Security
如何将 Spring Boot @RepositoryRestResource 映射到特定的 url?