首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何为Spring端点编写安全性测试

如何为Spring端点编写安全性测试
EN

Stack Overflow用户
提问于 2020-09-22 09:20:25
回答 1查看 714关注 0票数 0

我正在寻找一种合理的方法来测试我们的WebSocket安全实现。我们使用Spring TestRestTemplate来测试REST端点,这种方法似乎几乎适用于WebSockets。如果您接受BAD_REQUEST作为OK的替代品。

有一种测试的好方法吗?

这是我们的处理程序映射

代码语言:javascript
复制
@Configuration
class HandlerMappingConfiguration {
    @Bean
    fun webSocketMapping(webSocketHandler: DefaultWebSocketHandler?): HandlerMapping {
        val map: MutableMap<String, WebSocketHandler?> = HashMap()
        map["/"] = webSocketHandler
        map["/protected-ws"] = webSocketHandler

        val handlerMapping = SimpleUrlHandlerMapping()
        handlerMapping.order = 1
        handlerMapping.urlMap = map
        return handlerMapping
    }
}

这是我们的安全配置

代码语言:javascript
复制
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig(
    private val authenticationConverter: ServerAuthenticationConverter,
    @param:Value("\${spring.security.oauth2.resourceserver.jwt.issuer-uri}") private val issuerUri: String,
    @param:Value("\${spring.security.oauth2.resourceserver.jwt.audience}") private val audience: String
) {
    @Bean
    fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http
            .csrf().disable()
            .authorizeExchange()
            .pathMatchers("/protected-ws").hasAuthority("SCOPE_read:client-ws")
            .pathMatchers("/protected").hasAuthority("SCOPE_read:client")
            .matchers(EndpointRequest.toAnyEndpoint()).permitAll()
            .anyExchange().authenticated()
            .and()
            .oauth2ResourceServer()
            .bearerTokenConverter(authenticationConverter)
            .jwt()
        return http.build()
    }

    @Bean
    open fun jwtDecoder(): ReactiveJwtDecoder {
        val jwtDecoder = ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri) as NimbusReactiveJwtDecoder
        jwtDecoder.setJwtValidator(DelegatingOAuth2TokenValidator(withIssuer(), audienceValidator()))
        return jwtDecoder
    }

    private fun withIssuer(): OAuth2TokenValidator<Jwt>? {
        return JwtValidators.createDefaultWithIssuer(
            issuerUri
        )
    }

    private fun audienceValidator(): OAuth2TokenValidator<Jwt> {
        return JwtAudienceValidator(
            audience
        )
    }
}

这是REST端点的IntegrationTest

代码语言:javascript
复制
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RestSecurityTest(@Autowired val restTemplate: TestRestTemplate) {

    @Test
    fun `Unauthorized user is unauthorized`() {
        val entity = restTemplate.getForEntity<String>("/world", String::class.java)
        assertThat(entity.statusCode).isEqualTo(HttpStatus.UNAUTHORIZED)
    }

    @Test
    @WithMockUser(authorities = ["SCOPE_read:world"])
    fun `Authorized user, without required scope is Forbidden`() {
        val entity = restTemplate.getForEntity<String>("/protected", String::class.java)
        assertThat(entity.statusCode).isEqualTo(HttpStatus.FORBIDDEN)
    }

    @Test
    @WithMockUser(authorities = ["SCOPE_read:client"])
    fun `Authorized user, with required scope is OK`() {
        val entity = restTemplate.getForEntity<String>("/protected", String::class.java)
        assertThat(entity.statusCode).isEqualTo(HttpStatus.OK)
    }
}

我们希望为我们的WebSocket端点实现类似的目标.例如

代码语言:javascript
复制
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WebSocketSecurityTest(@Autowired val restTemplate: TestRestTemplate) {

    @Test
    fun `WebSocket unauthorized user is unauthorized`() {
        val entity = restTemplate.getForEntity<String>("/protected-ws", String::class.java)
        assertThat(entity.statusCode).isEqualTo(HttpStatus.UNAUTHORIZED)
    }

    @Test
    @WithMockUser(authorities = ["SCOPE_read:world"])
    fun `WebSocket authorized user, without required scope is forbidden`() {
        val entity = restTemplate.getForEntity<String>("/protected-ws", String::class.java)
        assertThat(entity.statusCode).isEqualTo(HttpStatus.FORBIDDEN)
    }

    @Test
    @WithMockUser(authorities = ["SCOPE_read:client-ws"])
    fun `WebSocket Authorized user, with required scope is OK`() {
        val entity = restTemplate.getForEntity<String>("/protected-ws", String::class.java)
        // You can't open a WebSocket connection with a REST client. But this shows the request is authorized.
        assertThat(entity.statusCode).isEqualTo(HttpStatus.BAD_REQUEST)
    }
}

但这有几个限制:

  1. 我们的OK测试必须检查BAD_REQUEST响应!
  2. 我们无法验证来自端点的任何实际有效载荷。

所以它只适用于失败场景。此示例的完整源代码可在github上获得。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-22 10:18:43

我发现了一种基于我所读到的STOMP测试的方法,我意识到STOMP客户端使用的是一个普通的WebSocket客户端,并且我发现我能够以同样的方式构建测试,而不用STOMP包装器。

代码语言:javascript
复制
@Test
@WithMockUser(authorities = ["SCOPE_read:client-ws"])
fun `WebSocket authorized user is able to connect to endpoint`() {
    val latch = CountDownLatch(1)
    StandardWebSocketClient().execute(
        URI.create(String.format("ws://localhost:%d/protected-ws", port)),
        TestClientHandler(latch)
    ).subscribe()
    assertThat(latch.await(60, TimeUnit.SECONDS)).isTrue()
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64006722

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档