Spring security - oauth2 资源服务器测试

Posted

技术标签:

【中文标题】Spring security - oauth2 资源服务器测试【英文标题】:Spring security - oauth2 resource server tests 【发布时间】:2019-11-27 22:05:50 【问题描述】:

我在使用 @WebMvcTest 和 POST HTTP 方法测试 oauth2 资源服务器时遇到了一些问题。

当我不发送 csrf 令牌时,我总是收到 403 状态代码,即使在我使用不记名令牌时不需要该令牌。

这是我要测试的 POST 方法。

@PostMapping("/message")
public String createMessage(@RequestBody String message) 
    return String.format("Message was created. Content: %s", message);


这是我的安全配置:

http.authorizeRequests(authorizeRequests -> authorizeRequests       
   .antMatchers("/message/**")
   .hasAuthority("SCOPE_message:read")
   .anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer ->               
    oauth2ResourceServer
    .jwt(withDefaults())
);

我正在关注spring-security 的样本中提供的测试。

以下测试本应通过,但由于请求中未发送 csrf 令牌而失败。

mockMvc.perform(post("/message").content("Hello message")
    .with(jwt(jwt -> jwt.claim("scope", "message:read")))
    .andExpect(status().isOk())
    .andExpect(content().string(is("Message was created. Content: Hello message")));

当我将 csrf 令牌添加到请求中时,测试通过:

mockMvc.perform(post("/message").content("Hello message")
    .with(jwt(jwt -> jwt.claim("scope", "message:read")))
    .with(csrf()))
    .andExpect(status().isOk())
    .andExpect(content().string(is("Message was created. Content: Hello message")));

当我运行应用程序时,不需要在 POST 请求中发送 csrf 令牌。

我已经分叉了 Spring Security GitHub 存储库,这个测试失败的项目可以在 link 找到。

有没有办法让我配置我的测试,这样我就不需要在 POST 请求中发送 csrf 令牌?

【问题讨论】:

您说“当我运行应用程序时,不需要在 POST 请求中发送 csrf 令牌。”,您是如何尝试的?所有 POST 请求都需要 CSRF 令牌,除非在您的安全配置中使用 .csrf(csrf -> csrf.disable()) 明确禁用。示例项目测试都是针对 GET 请求的,因此它们不需要 CSRF 令牌。 其实运行时不需要CSRF token。当我运行以下命令时:curl -v -H "Authorization: Bearer $TOKEN" -d "my message" -X POST localhost:8080/message 我收到 200 状态。据我了解,CSRF 过滤器仅在没有 Bearer 令牌时才检查令牌。 我的错误,如果有承载令牌,则不需要 CSRF 令牌是正确的。仅包含 jwt 后处理器的测试的问题在于,虽然这会创建包含 JWT 的安全上下文,但它不会在请求中创建 Bearer 令牌(这是 CSRF 过滤器正在寻找的)。跨度> 【参考方案1】:

为了让 CSRF 过滤器检测到您正在使用 JWT 令牌,您需要将 JWT 令牌作为 Authorization 标头或请求参数包含在您的请求中。 您提到的测试有一个模拟 JwtDecoder,这意味着您可以使用任何字符串作为您的令牌并模拟解码后的值。 然后您的测试将变为:

Jwt jwt = Jwt.withTokenValue("token")
        .header("alg", "none")
        .claim("scope", "message:read")
        .build();
when(jwtDecoder.decode(anyString())).thenReturn(jwt);
mockMvc.perform(post("/message")
        .content("Hello message")
        .header("Authorization", "Bearer " + jwt.getTokenValue()))
        .andExpect(status().isOk())
        .andExpect(content().string(is("Message was created. Content: Hello message")));

如果您没有模拟JwtDecoder,那么您需要检索一个有效的不记名令牌并将其传递到Authorization 标头中。

【讨论】:

成功了,谢谢!但是不能在 POST 请求中使用 JwtRequestPostProcessor 吗?测试GET方法的代码更简洁。 @henriquels JwtRequestPostProcessor 不添加 CSRF 过滤器所需的标头。你可以编写一个自定义的后处理器,类似于this one。

以上是关于Spring security - oauth2 资源服务器测试的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Security OAuth2 设置 - 无法找到 oauth2 命名空间处理程序

Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt

如何使用spring-security,oauth2调整实体的正确登录?

针对授权标头的Spring Security OAuth2 CORS问题

Spring Security---Oauth2详解

OAuth2.0学习(4-1)Spring Security OAuth2.0 - 代码分析