【问题标题】:spring boot keycloak integration testspring boot keycloak 集成测试
【发布时间】:2020-04-27 13:27:24
【问题描述】:

我正在使用 keycloak 来验证我的 Spring Boot 应用程序,如下所示:

@Configuration
public class CustomKeycloakSpringBootConfigResolver extends KeycloakSpringBootConfigResolver {
    private final KeycloakDeployment keycloakDeployment;
    CustomKeycloakSpringBootConfigResolver(KeycloakSpringBootProperties properties) {
        keycloakDeployment = KeycloakDeploymentBuilder.build(properties);
    }

    @Override
    public KeycloakDeployment resolve(HttpFacade.Request facade) {
        return keycloakDeployment;
    }

@KeycloakConfiguration
class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    @Autowired
    void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Override
    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/account/**").hasRole("user")
                .anyRequest().permitAll().and()
                .csrf().disable();
    }

    @Bean
    @Override
    @ConditionalOnMissingBean(HttpSessionManager.class)
    protected HttpSessionManager httpSessionManager() {
        return new HttpSessionManager();
    }

}

我只需要使用mockmvc编写集成测试,它将测试每当访问安全资源时,将触发身份验证到keycloak,并在成功身份验证后返回资源。

任何人都可以建议如何实现这一目标。

【问题讨论】:

  • 不要评论为什么我的回答不能解决你的问题(或接受它......)

标签: spring-boot spring-security keycloak


【解决方案1】:

正如this answer 中已经提到的,我在SecurityContext 中写了a lib to ease unit testsKeycloakAuthenticationToken

您可以从此处浏览带有@Controller@Service 单元测试的完整示例应用程序:https://github.com/ch4mpy/spring-addons/tree/master/spring-security-oauth2-test-webmvc-addons/src/test/java/com/c4_soft/springaddons/samples/webmvc/keycloak

KeycloakMessageServiceTest:

@RunWith(SpringRunner.class)
@Import(KeycloakMessageServiceTest.TestConfig.class)
public class KeycloakMessageServiceTest {

    @Autowired
    MessageService service;

    @Test(expected = AccessDeniedException.class)
    @WithMockKeycloakAuth(authorities = "USER", oidc = @OidcStandardClaims(preferredUsername = "ch4mpy"))
    public void whenAuthenticatedWithoutAuthorizedPersonnelThenCanNotGetSecret() {
        service.getSecret();
    }

    @Test()
    @WithMockKeycloakAuth(
            authorities = "AUTHORIZED_PERSONNEL",
            oidc = @OidcStandardClaims(preferredUsername = "ch4mpy"))
    public void whenAuthenticatedWitAuthorizedPersonnelThenGetSecret() {
        final var actual = service.getSecret();
        assertEquals("Secret message", actual);
    }

    @Test(expected = Exception.class)
    public void whenNotAuthenticatedThenCanNotGetGreeting() {
        service.greet(null);
    }

    @Test()
    @WithMockKeycloakAuth(
            authorities = "AUTHORIZED_PERSONNEL",
            oidc = @OidcStandardClaims(preferredUsername = "ch4mpy"))
    public void whenAuthenticatedThenGetGreeting() {
        final var actual =
                service.greet((KeycloakAuthenticationToken) SecurityContextHolder.getContext().getAuthentication());
        assertEquals("Hello ch4mpy! You are granted with [AUTHORIZED_PERSONNEL].", actual);
    }

    @TestConfiguration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @Import({ KeycloakMessageService.class })
    public static class TestConfig {
        @Bean
        public GrantedAuthoritiesMapper authoritiesMapper() {
            return new NullAuthoritiesMapper();
        }
    }
}

GreetingControllerAnnotatedTest:

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = GreetingController.class)
@Import({
        ServletKeycloakAuthUnitTestingSupport.UnitTestConfig.class,
        KeycloakSpringBootSampleApp.KeycloakConfig.class })
// because this sample stands in the middle of non spring-boot-keycloak projects, keycloakproperties are isolated in
// application-keycloak.properties
@ActiveProfiles("keycloak")
public class GreetingControllerAnnotatedTest {
    private static final String GREETING = "Hello %s! You are granted with %s.";

    @MockBean
    MessageService messageService;

    @MockBean
    JwtDecoder jwtDecoder;

    @Autowired
    MockMvcSupport api;

    @Before
    public void setUp() {
        when(messageService.greet(any())).thenAnswer(invocation -> {
            final var auth = invocation.getArgument(0, Authentication.class);
            return String.format(GREETING, auth.getName(), auth.getAuthorities());
        });
    }

    @Test
    @WithMockKeycloakAuth(
            authorities = { "USER", "AUTHORIZED_PERSONNEL" },
            id = @IdTokenClaims(sub = "42"),
            oidc = @OidcStandardClaims(
                    email = "ch4mp@c4-soft.com",
                    emailVerified = true,
                    nickName = "Tonton-Pirate",
                    preferredUsername = "ch4mpy"),
            accessToken = @KeycloakAccessToken(
                    realmAccess = @KeycloakAccess(roles = { "TESTER" }),
                    authorization = @KeycloakAuthorization(
                            permissions = @KeycloakPermission(rsid = "toto", rsname = "truc", scopes = "abracadabra"))),
            privateClaims = @ClaimSet(stringClaims = @StringClaim(name = "foo", value = "bar")))
    public void whenAuthenticatedWithKeycloakAuthenticationTokenThenCanGreet() throws Exception {
        api.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")))
                .andExpect(content().string(containsString("TESTER")));
    }

    @Test
    @WithMockKeycloakAuth(authorities = { "USER" }, oidc = @OidcStandardClaims(preferredUsername = "ch4mpy"))
    public void whenAuthenticatedWithoutAuthorizedPersonnelThenSecuredRouteIsForbidden() throws Exception {
        api.get("/secured-route").andExpect(status().isForbidden());
    }

    @Test
    @WithMockKeycloakAuth(
            authorities = { "AUTHORIZED_PERSONNEL" },
            oidc = @OidcStandardClaims(preferredUsername = "ch4mpy"))
    public void whenAuthenticatedWithAuthorizedPersonnelThenSecuredRouteIsOk() throws Exception {
        api.get("/secured-route").andExpect(status().isOk());
    }
}

【讨论】:

  • 谢谢。这确实帮助我获得了一个好主意,并且我能够编写集成测试。再次感谢。
  • 谢谢,很好的解决方案。
猜你喜欢
  • 2020-01-11
  • 2020-02-25
  • 1970-01-01
  • 2019-04-28
  • 1970-01-01
  • 2015-08-20
  • 2020-04-24
  • 2017-10-27
  • 2017-06-28
相关资源
最近更新 更多