Mocking a Keycloak token for testing a Spring controller

后端 未结 2 1294
星月不相逢
星月不相逢 2021-02-13 17:46

I want to write unit tests for my spring controller. I\'m using keycloak\'s openid flow to secure my endpoints.

In my tests I\'m using the @WithMockUser ann

2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2021-02-13 18:22

    @WithmockUser configures the security-context with a UsernamePasswordAuthenticationToken. This can be just fine for most use-cases but when your app relies on another Authentication implementation (like your code does), you have to build or mock an instance of the right type and put it in the test security-context: SecurityContextHolder.getContext().setAuthentication(authentication);

    Of course, you'll soon want to automate this, building your own annotation or RequestPostProcessor

    ... or ...

    take one "off the shelf", like in this lib of mine, which is available from maven-central:

    
        com.c4-soft.springaddons
        spring-security-oauth2-test-webmvc-addons
        2.3.4
        test
    
    

    You can use it either with @WithMockKeycloackAuth annotations:

    @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")));
        }
    

    Or 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")));
        }
    
    }
    

提交回复
热议问题