1. 概述

Keycloak 是一个免费且开源的身份和访问管理程序,常常在当今的软件栈中使用。在测试阶段,我们可能会希望禁用它的使用,以便专注于业务测试。测试环境中可能也没有Keycloak服务器。

在这个教程中,我们将移除由Keycloak启动器设置的配置,还会探讨在项目中启用Keycloak时如何修改Spring Security。

2. 在非Spring Security环境中禁用Keycloak

首先,我们来看看如何在一个不使用Spring Security的应用中禁用Keycloak。

2.1. 应用设置

我们从在项目中添加*spring-boot-starter-oauth2-client*依赖开始:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

此外,我们需要添加*spring-boot-starter-oauth2-resource-server*依赖。这将允许我们在Keycloak服务器上验证JWT令牌。因此,让我们将其添加到pom.xml:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

接下来,我们在application.properties中添加Keycloak服务器的配置:

spring.security.oauth2.client.registration.keycloak.client-id=login-app
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=
    http://localhost:8080/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak

最后,我们添加一个UserController来获取用户:

@RestController
@RequestMapping("/users")
public class UserController {
    @GetMapping("/{userId}")
    public User getCustomer(@PathVariable(name = "userId") Long userId) {
        return new User(userId, "John", "Doe");
    }
}

2.2. 禁用Keycloak

现在我们的应用已经就绪,让我们编写一个简单的测试来获取用户:

@Test
public void givenUnauthenticated_whenGettingUser_shouldReturnUser() {
    ResponseEntity<User> responseEntity = restTemplate.getForEntity("/users/1", User.class);

    assertEquals(HttpStatus.SC_OK, responseEntity.getStatusCodeValue());
    assertNotNull(responseEntity.getBody()
        .getFirstname());
}

这个测试会失败,因为restTemplate没有提供任何身份验证,或者Keycloak服务器不可用。

Keycloak适配器实现了Keycloak安全的Spring自动配置。自动配置依赖于类路径中的类或属性值。特别是,对于这种特定需求,[@ConditionalOnProperty注解]非常有用。

为了禁用Keycloak安全,我们需要告知适配器不应加载相应的配置。我们可以这样做,如下所示:

keycloak.enabled=false

再次运行测试,它现在会在没有任何身份验证的情况下成功。

3. 在Spring Security环境中禁用Keycloak

我们经常使用Keycloak与Spring Security配合。在这种情况下,仅仅禁用Keycloak配置是不够的,还需要修改Spring Security配置以允许匿名请求到达控制器

3.1. 应用设置

首先,我们向项目添加spring-boot-starter-security依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

接下来,我们创建一个SecurityFilterChain bean来定义Spring Security所需的配置:

@Configuration
@EnableWebSecurity
public class KeycloakSecurityConfig {

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth -> auth.anyRequest()
                .permitAll())
            .oauth2Login(Customizer.withDefaults())
            .oauth2ResourceServer(httpSecurityOAuth2ResourceServerConfigurer -> 
                httpSecurityOAuth2ResourceServerConfigurer.jwt(Customizer.withDefaults()));
        return http.build();
    }
}

在这里,我们配置Spring Security只允许已认证用户的请求。

3.2. 禁用Keycloak

除了像之前那样禁用Keycloak外,现在我们还需要禁用Spring Security

我们可以使用profile告诉Spring在测试时是否激活Keycloak配置:

@Configuration
@EnableWebSecurity
@Profile("tests")
public class KeycloakSecurityConfig {
    // ...
}

然而,更优雅的方法是重用keycloak.enable属性,类似于Keycloak适配器:

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "keycloak.enabled", havingValue = "true", matchIfMissing = true)
public class KeycloakSecurityConfig {
    // ...
}

因此,如果keycloak.enable属性为true,Spring才会启用Keycloak配置。如果属性缺失,matchIfMissing默认启用它。

由于我们使用了Spring Security启动器,仅仅禁用我们的Spring Security配置是不够的。实际上,遵循Spring的主观默认配置原则,启动器会创建一个默认的安全层

让我们创建一个配置类来禁用它:

@Configuration
@ConditionalOnProperty(name = "keycloak.enabled", havingValue = "false")
public class DisableSecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(request -> request.anyRequest()
                .permitAll());
        return http.build();
    }

}

我们仍然使用keycloak.enable属性,但这次Spring会在其值为false时启用配置。

4. 总结

在这篇文章中,我们探讨了如何在Spring环境中禁用Keycloak安全,无论是否使用Spring Security。

如往常一样,本文中使用的所有代码示例可以在GitHub上找到。