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