【问题标题】:Mocking a Keycloak token for testing a Spring controller模拟 Keycloak 令牌以测试 Spring 控制器
【发布时间】:2018-08-15 03:27:48
【问题描述】:

我想为我的 spring 控制器编写单元测试。我正在使用 keycloak 的 openid 流来保护我的端点。

在我的测试中,我使用 @WithMockUser 注释来模拟经过身份验证的用户。我的问题是我正在从委托人的令牌中读取 userId。我的单元测试现在失败了,因为我从令牌中读取的userId 为空;

        if (principal instanceof KeycloakAuthenticationToken) {
            KeycloakAuthenticationToken authenticationToken = (KeycloakAuthenticationToken) principal;
            SimpleKeycloakAccount account = (SimpleKeycloakAccount) authenticationToken.getDetails();
            RefreshableKeycloakSecurityContext keycloakSecurityContext = account.getKeycloakSecurityContext();
            AccessToken token = keycloakSecurityContext.getToken();
            Map<String, Object> otherClaims = token.getOtherClaims();
            userId = otherClaims.get("userId").toString();
        }

有什么可以轻松模拟KeycloakAuthenticationToken吗?

【问题讨论】:

  • 您能找到任何解决方案吗?如果是,请分享您的解决方案。您能否也分享一个示例集成测试。我也无法找到使用模拟密钥斗篷进行集成测试的方法。谢谢
  • 我也在相同的技术堆栈上开始 IT 测试,但在 key cloak mocking 或至少将 key cloak 嵌入到我的应用程序中然后使用它时失败了。如果他成功了,任何人都可以提供帮助。

标签: spring junit spring-security mockito keycloak


【解决方案1】:

@WithmockUser 使用UsernamePasswordAuthenticationToken 配置安全上下文。这对于大多数用例来说都很好,但是当您的应用程序依赖于另一个身份验证实现(就像您的代码一样)时,您必须构建或模拟正确类型的实例并将其放入测试安全上下文中:@987654325 @

当然,您很快就会希望自动执行此操作,构建自己的注释或RequestPostProcessor

...或...

从 maven-central 获得一个“现成的”,例如 lib of mine

<dependency>
    <!-- just enough for @WithMockKeycloackAuth -->
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-security-oauth2-test-addons</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <!-- required only for WebMvc "fluent" API -->
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-security-oauth2-test-webmvc-addons</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>

您可以将其与@WithMockKeycloackAuth 注释一起使用:

@RunWith(SpringRunner.class)
@WebMvcTest(GreetingController.class)
@ContextConfiguration(classes = GreetingApp.class)
@ComponentScan(basePackageClasses = { KeycloakSecurityComponents.class, KeycloakSpringBootConfigResolver.class })
public class GreetingControllerTests extends ServletUnitTestingSupport {
    @MockBean
    MessageService messageService;

    @Test
    @WithMockKeycloackAuth("TESTER")
    public void whenUserIsNotGrantedWithAuthorizedPersonelThenSecretRouteIsNotAccessible() throws Exception {
        mockMvc().get("/secured-route").andExpect(status().isForbidden());
    }

    @Test
    @WithMockKeycloackAuth("AUTHORIZED_PERSONNEL")
    public void whenUserIsGrantedWithAuthorizedPersonelThenSecretRouteIsAccessible() throws Exception {
        mockMvc().get("/secured-route").andExpect(content().string(is("secret route")));
    }

    @Test
    @WithMockKeycloakAuth(
            authorities = { "USER", "AUTHORIZED_PERSONNEL" },
            claims = @OpenIdClaims(
                    sub = "42",
                    email = "ch4mp@c4-soft.com",
                    emailVerified = true,
                    nickName = "Tonton-Pirate",
                    preferredUsername = "ch4mpy",
                    otherClaims = @Claims(stringClaims = @StringClaim(name = "foo", value = "bar"))))
    public void whenAuthenticatedWithKeycloakAuthenticationTokenThenCanGreet() throws Exception {
        mockMvc().get("/greet")
                .andExpect(status().isOk())
                .andExpect(content().string(startsWith("Hello ch4mpy! You are granted with ")))
                .andExpect(content().string(containsString("AUTHORIZED_PERSONNEL")))
                .andExpect(content().string(containsString("USER")));
    }

或 MockMvc fluent API (RequestPostProcessor):

@RunWith(SpringRunner.class)
@WebMvcTest(GreetingController.class)
@ContextConfiguration(classes = GreetingApp.class)
@ComponentScan(basePackageClasses = { KeycloakSecurityComponents.class, KeycloakSpringBootConfigResolver.class })
public class GreetingControllerTest extends ServletKeycloakAuthUnitTestingSupport {
    @MockBean
    MessageService messageService;

    @Test
    public void whenUserIsNotGrantedWithAuthorizedPersonelThenSecretMethodIsNotAccessible() throws Exception {
        mockMvc().with(authentication().roles("TESTER")).get("/secured-method").andExpect(status().isForbidden());
    }

    @Test
    public void whenUserIsGrantedWithAuthorizedPersonelThenSecretMethodIsAccessible() throws Exception {
        mockMvc().with(authentication().roles("AUTHORIZED_PERSONNEL")).get("/secured-method")
                .andExpect(content().string(is("secret method")));
    }

}

【讨论】:

  • 不知道为什么这被否决了。我使用版本 2.0.0 和 spring boot 2.2.6 和 keycloak 9.0.0 对此进行了测试,它工作正常。
  • 感谢您的反馈 ;)
  • 像魅力一样工作。无法访问刷新令牌,但我会深入研究一下。
  • @ch4mp 这个库支持模拟客户端角色吗?我有受特定客户端角色保护的端点,所以我需要用它来模拟用户。谢谢
  • 我的库允许您设置任何值的任何声明。如果您在idoidc 下找不到标准声明,您可以查看accessToken 下的 Keycloak 私有声明,或在otherClaims 下设置您自己的私有声明。我的猜测是您正在寻找的内容已在测试中得到证明:github.com/ch4mpy/spring-addons/blob/…
猜你喜欢
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-29
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多