【问题标题】:Ignore Bearer Token Validation for specific Urls in Spring Boot忽略 Spring Boot 中特定 Url 的承载令牌验证
【发布时间】: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 标头,您是在告诉后端“嗨,这是我的请求,这是证明我是谁的标头”。现在您想发送一个身份验证标头,即使您不想要或不应该,而是忽略它?对我来说,这听起来像是给自己制造更多问题。我在春天看不到任何明显的东西可以让你做到这一点,而且很确定春天同意我的看法。如果您不想被授权,请不要发送授权标头。

标签: java spring-security jwt spring-webflux spring-security-oauth2


【解决方案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
猜你喜欢
  • 2017-09-13
  • 2018-06-22
  • 2018-01-17
  • 2020-01-18
  • 2015-12-18
  • 2022-01-23
  • 2018-09-30
  • 2020-04-23
  • 2019-11-19
相关资源
最近更新 更多