模拟Keycloak令牌以测试Spring控制器

时间:2018-03-07 05:47:00

标签: spring junit spring-security mockito keycloak

我想为我的弹簧控制器编写单元测试。我使用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吗?

2 个答案:

答案 0 :(得分:5)

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

当然,您很快会希望自动化它,构建自己的注释或SecurityContextHolder.getContext().setAuthentication(authentication);

...或...

像现成的lib of mine一样“下架”,可从maven-central获得:

RequestPostProcessor

您可以将其与<dependency> <groupId>com.c4-soft.springaddons</groupId> <artifactId>spring-security-oauth2-test-webmvc-addons</artifactId> <version>2.3.4</version> <scope>test</scope> </dependency> 批注一起使用:

@WithMockKeycloackAuth

或MockMvc流利的API(RequestPostProcessor):

@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" },
            id = @IdTokenClaims(sub = "42"),
            oidc = @OidcStandardClaims(
                    email = "ch4mp@c4-soft.com",
                    emailVerified = true,
                    nickName = "Tonton-Pirate",
                    preferredUsername = "ch4mpy"),
            privateClaims = @ClaimSet(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")));
    }

答案 1 :(得分:0)

添加以下内容后,我可以进行测试:

  1. 添加一些字段:

    @Autowired
    
        private WebApplicationContext context:
        private MockMvc mockMvc;
    
  2. 在我的@Before setup()方法中:

    mockMvc = MockMvcBuilders.webAppContextSetup(context)
       .alwaysDo(print())
       .apply(springSecurity())
       .build(); 
    
  3. 在我的测试方法中:

    SecurityContext context = SecurityContextHolder.getContext();
    Authentication authentication = context.getAuthentication();
    

当我将authentication对象传递给从密钥斗篷令牌中读取声明的方法时,我可以毫无问题地运行测试。

P.S。不要忘记@WithMockUser批注

相关问题