在 Spring Boot 中禁用 Keycloak Security

1、概览

Keycloak 是一个开源的身份和访问管理解决方案。在测试阶段,禁用 Keycloak 可能有助于专注于业务测试。而且在测试环境中可能没有 Keycloak 服务器。

本文将带你了解如何禁用 Keycloak starter 的配置,以及如何在项目中启用 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 Token:

<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 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 提供任何 Authentication(身份认证),或者 Keycloak 服务器不可用,因此该测试将失败。

Keycloak adapter 实现了 Keycloak Security 的 Spring 自动配置。自动配置依赖于类路径中存在的类或属性值。具体而言,@ConditionalOnProperty 注解非常适用于这个特定的需求。

要禁用 Keycloak Security,我们需要通知 adapter 不要加载相应的配置。为此,我们可以设置如下属性:

keycloak.enabled=false

再次启动测试,现在无需任何 Authentication 即可成功。

3、在 Spring Security 环境下禁用 Keycloak

我们经常将 Keycloak 与 Spring Security 结合使用。在这种情况下,仅禁用 Keycloak 配置是不够的,还需要修改 Spring Security 配置,以允许匿名请求访问 Controller。

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()
            .disable()
            .authorizeHttpRequests(auth -> auth.anyRequest()
                .authenticated());
        http.oauth2Login();
        http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return http.build();
    }
}

这里,对 Spring Security 进行配置,使其只允许来自已认证用户的请求。

3.2、禁用 Keycloak

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

我们可以使用 profiles 来告诉 Spring 在测试期间是否激活 Keycloak 配置:

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

不过,更优雅的方法是重复使用 keycloak.enable 属性,类似于 Keycloak adapter:

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

因此,只有当 keycloak.enable 属性为 true 时,Spring 才会启用 Keycloak 配置。如果缺少该属性,则 matchIfMissing 会默认启用它。

由于我们使用的是 Spring Security Starter,因此仅禁用 Spring Security 配置是不够的。事实上,根据 Spring 的默认配置原则,Starter 将创建一个默认的安全层(Security Layer)。

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

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf()
            .disable()
            .authorizeRequests()
            .anyRequest()
            .permitAll();
       return http.build();
    }
}

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

4、总结

本文介绍了如何在 Spring 中禁用 Keycloak,包括使用 Spring Security 和未使用 Spring Security 的情况。


参考:https://www.baeldung.com/spring-keycloak-security-disable