一、简介
在验证应用程序是否正常工作时,集成测试至关重要。 此外,我们应该正确测试身份验证,因为它是一个敏感部分。 Testcontainers 允许我们在测试阶段启动 Docker 容器,以针对实际技术堆栈运行我们的测试。
在本文中,我们将了解如何使用 Testcontainers 针对实际的 Keycloak 实例设置集成测试。
2. 使用 Keycloak 设置 Spring Security
我们需要设置 Spring Security、Keycloak 配置,最后是 Testcontainers。
2.1。 设置 Spring Boot 和 Spring Security
感谢 Spring Security,让我们从设置安全性开始。 我们需要spring-boot-starter-security依赖项。 让我们将它添加到我们的 pom 中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
我们将使用 spring-boot 父 pom。 因此,我们不需要指定在其依赖项管理中指定的库的版本。
接下来,让我们创建一个简单的控制器来返回一个用户:
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("me")
public UserDto getMe() {
return new UserDto(1L, "janedoe", "Doe", "Jane", "[email protected]");
}
}
此时,我们有一个安全控制器来响应“ /users/me”
上的请求。 启动应用程序时,Spring Security 为用户“user”生成密码,在应用程序日志中可见。
2.2.配置密钥斗篷
启动本地 Keycloak 的最简单方法是使用 Docker 。 让我们运行一个已配置管理员帐户的 Keycloak 容器:
docker run -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:17.0.1 start-dev
让我们打开浏览器访问 URL http://localhost:8081 以访问 Keycloak 控制台:
接下来,让我们创建我们的领域。 我们称之为baeldung:
我们需要添加一个客户端,我们将其命名为 baeldung-api:
最后,让我们使用“用户”菜单添加 Jane Doe 用户:
现在我们已经创建了用户,我们必须为它分配一个密码。 让我们选择 s3cr3t 并取消选中临时按钮 :
我们现在已经使用 baeldung-api 客户端和 Jane Doe 用户设置了我们的 Keycloak 领域。
我们接下来将配置Spring以使用Keycloak作为身份提供者。
2.3.将两者放在一起
首先,我们将识别控制委托给 Keycloak 服务器。 为此,我们将使用一个方便的启动器keycloak-spring-boot-starter 。 因此,让我们将它添加到我们的 pom 中:
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
我们还需要keycloak-adapter-bom依赖项。 具体来说,它添加了主适配器以充分使用 Spring 自动配置,以及连接 Keycloak 和不同 Web 容器(包括 Tomcat)所需的库:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak-adapter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后,让我们创建一个使用 Spring 属性来配置 Keycloak 适配器的配置类。
@Configuration
public class KeycloakConfiguration {
@Bean
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
让我们继续配置 Spring Security 以使用 Keycloak 配置:
@KeycloakConfiguration
@ConditionalOnProperty(name = "keycloak.enabled", havingValue = "true", matchIfMissing = true)
public class KeycloakSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf()
.disable()
.cors()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
我们正在构建一个带有仅承载身份验证的无状态应用程序。 出于这个原因,我们将使用NullAuthenticatedSessionStrategy
作为会话策略。 此外, 允许我们通过将属性设置为来禁用 Keycloak 配置。@ConditionalOnProperty
keycloak.enable
false
最后,让我们在文件中添加连接 Keycloak 所需的配置:application.properties
keycloak.enabled=true
keycloak.realm=baeldung
keycloak.resource=baeldung-api
keycloak.auth-server-url=http://localhost:8081
我们的应用程序现在是安全的,并在每个请求上查询 Keycloak 以验证身份验证。
3. 为 Keycloak 设置测试容器
3.1。 导出领域配置
Keycloak 容器在没有任何配置的情况下启动。 因此,我们必须在容器作为 JSON 文件启动时导入它。 让我们从当前运行的实例中导出这个文件:
不幸的是,Keycloak 不会导出用户。 在这种情况下,我们必须手动编辑生成的文件并将我们的 Jane Doe 添加到其中。 让我们在最后一个花括号之前添加这个配置:realm-export.json
"users": [
{
"username": "janedoe",
"email": "[email protected]",
"firstName": "Jane",
"lastName": "Doe",
"enabled": true,
"credentials": [
{
"type": "password",
"value": "s3cr3t"
}
],
"clientRoles": {
"account": [
"view-profile",
"manage-account"
]
}
}
]
让我们将文件包含到项目中的文件夹中。 我们将在启动 Keycloak 容器时使用它。realm-export.json
src/test/resources/keycloak
3.2.设置测试容器
让我们添加testcontainers依赖项以及testcontainers-keycloak ,它允许我们启动 Keycloak 容器:
<dependency>
<groupId>com.github.dasniko</groupId>
<artifactId>testcontainers-keycloak</artifactId>
<version>2.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.16.3</version>
</dependency>
接下来,让我们创建一个类,我们的所有测试都将从该类派生。 我们使用它来配置由 Testcontainers 启动的 Keycloak 容器:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class IntegrationTest {
static final KeycloakContainer keycloak = new KeycloakContainer().withRealmImportFile("keycloak/realm-export.json");
}
静态声明我们的容器将确保它为我们所有的测试实例化一次。 我们使用 KeycloakContainer 对像中的withRealmImportFile
方法指定要在启动时导入的领域配置KeycloakContainer
3.3. Spring Boot 测试配置
现在,让我们在测试开始时启动 Keycloak 容器。 它使用随机端口。 因此,一旦启动,我们需要覆盖在中定义的配置。 为此,我们将在刷新上下文之前实现一个由 Spring 触发的回调接口:application.properties
keycloak.auth-server-url
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
keycloak.start();
TestPropertyValues.of("keycloak.auth-server-url=" + keycloak.getAuthServerUrl())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
我们还需要告诉 Spring 使用这个类来初始化它的上下文。 让我们在类级别添加此注释:
@ContextConfiguration(initializers = { IntegrationTest.Initializer.class })
4. 创建集成测试
现在我们已经有了负责启动 Keycloak 容器和配置 Spring 属性的主要测试类,让我们创建一个调用控制器的集成测试。User
4.1。 获取访问令牌
首先,让我们在抽像类 IntegrationTest 中添加一个使用 Jane Doe 凭证请求令牌的方法:
URI authorizationURI = new URIBuilder(keycloak.getAuthServerUrl() + "/realms/baeldung/protocol/openid-connect/token").build();
WebClient webclient = WebClient.builder().build();
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.put("grant_type", Collections.singletonList("password"));
formData.put("client_id", Collections.singletonList("baeldung-api"));
formData.put("username", Collections.singletonList("[email protected]"));
formData.put("password", Collections.singletonList("s3cr3t"));
String result = webclient.post()
.uri(authorizationURI)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData))
.retrieve()
.bodyToMono(String.class)
.block();
在这里,我们使用Webflux的WebClient来发布一个包含获取访问令牌所需的不同参数的表单。
最后,我们将解析 Keycloak 服务器响应以从中提取令牌。 具体来说,我们生成一个包含关键词的经典身份验证字符串,然后是令牌的内容,准备在头中使用:Bearer
JacksonJsonParser jsonParser = new JacksonJsonParser();
return "Bearer " + jsonParser.parseMap(result)
.get("access_token")
.toString();
4.2.创建集成测试
让我们针对我们配置的 Keycloak 容器快速设置集成测试。 我们将使用RestAssured和Hamcrest进行测试。 让我们添加放心依赖:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
我們現在可以使用抽象的類创建我們的測試:IntegrationTest
@Test
void givenAuthenticatedUser_whenGetMe_shouldReturnMyInfo() {
given().header("Authorization", getJaneDoeBearer())
.when()
.get("/users/me")
.then()
.body("username", equalTo("janedoe"))
.body("lastname", equalTo("Doe"))
.body("firstname", equalTo("Jane"))
.body("email", equalTo("[email protected]"));
}
结果,我们从 Keycloak 获取的访问令牌被添加到请求的 Authorization 头中。
5.结论
在本文中,我们针对由 Testcontainers 管理的实际 Keycloak 设置集成测试。 每次启动测试阶段时,我们都会导入一个领域配置以拥有一个预配置的环境。
0 评论