KotlinSping Boot TestRestTemplate和OAuth2测试

qco9c6ql  于 2023-05-23  发布在  Kotlin
关注(0)|答案(1)|浏览(322)

我一直在尝试编写一个Kotling Sping Boot 测试,利用TestRestTemplate来调用我的应用程序上的一个端点,该端点上有一个PreAuthorize注解,但我无法向授权的OAuth2用户传播SecurityContext。我一直得到以下异常:
在SecurityContext中找不到身份验证对象
我试过几种不同的方法。下面,我将描述原始的设置,以及我所尝试的:

控制器

@RestController
@RequestMappting(/api/v1)
@PreAuthorize("@authorizationService.canDoThat()") // Some logic to compare request to backend DB. Test does not hit this method
class SomeController() {
   ...
   ...

   @PutMapping(/update)
   fun update(@RequestBody request) {
     // Some logic to update something (test doesn't hit this part)
   }
}

测试

@Test
fun `update`() {
   val url = "http://localhost:${port}${basePath}/update"

   val headers = HttpHeaders()
   val body = HttpEntity(
      UpdateRequest(
         // The request object
      ), headers
   )

   val response = restTemplate.exchange(
        url,
        HttpMethod.PUT,
        body,
        typeReference<SomeResponseType>()
    )
}

使用WithMockUser测试

@Test
@WithMockUser(username = "username", password = "password", authorities = ["SCOPE_write"])
fun `update`() {
   val url = "http://localhost:${port}${basePath}/update"

   val headers = HttpHeaders()
   val body = HttpEntity(
      UpdateRequest(
         // The request object
      ), headers
   )

   val response = restTemplate.exchange(
        url,
        HttpMethod.PUT,
        body,
        typeReference<SomeResponseType>()
    )
}

测试手动设置上下文

@Test
fun `update`() {
   val url = "http://localhost:${port}${basePath}/update"

   val jwt = Jwt(
         "token-value",
         Instant.now(),
         Instant.now().plusSeconds(60),
         mapOf("Authorization" to "Bearer token-value"),
         mapOf("someClaim" to someClaimValue)
   )
   val authorities = setOf(GrantedAuthority { "SCOPE_write" })
   val oauthAuthentication = JwtAuthenticationToken(jwt, authorities)

   // Set security context
   val context: SecurityContext = SecurityContextHolder.createEmptyContext()
   context.authentication = oauthAuthentication
   SecurityContextHolder.setContext(context)

   val headers = HttpHeaders()
   val body = HttpEntity(
      UpdateRequest(
         // The request object
      ), headers
   )

   val response = restTemplate.exchange(
        url,
        HttpMethod.PUT,
        body,
        typeReference<SomeResponseType>()
    )
}
e5nqia27

e5nqia271#

首先,很难弄清楚为什么使用RestTemplate而不是MockMvcWebTestClient(需要完整的测试类)。这是一个端到端的测试吗?
如果要编写端到端测试,您应该从授权服务器获取JWT:如果您自己构建它,它可能无法通过验证(JWT在授权服务器上使用私钥签名,JWT解码器使用公钥检查此签名)。在这种情况下,授权中的任何内容都不会被模仿,身份只是授权服务器所服务的身份。
如果编写单元(@WebMvcTest)或集成(@SpringBootTest)测试,则应使用MockMvcWebTestClient而不是RestTemplate。在这种情况下,使用注解MockMvc request postprocessor或WebTestClient mutator来构建正确的Authentication类型
其次,@WithMockUserUsernamePasswordAuthenticationToken示例放在测试安全上下文中,而您可能需要JwtAuthenticationToken(需要完整的安全配置才能确定)。您应该使用those libs I created中的@WithMockJwtAuth来构建JwtAuthentication token(spring-security-test为JWT提供的测试工具仅限于MockMvc和WebTestClient)。
我建议你花时间读this Baeldung article I wrote

相关问题