1. 概述
Keycloak 是一个专注于现代应用和服务的开源身份和访问管理(IAM)解决方案。当用户通过Keycloak进行身份验证时,服务器发出的令牌包含有关已认证用户和令牌发行给的客户端的重要信息。
Keycloak的令牌包含一些默认属性,如 iss(签发者)、exp(过期时间)、sub(主体)和 aud(受众)。然而,这些往往不够,我们可能需要在令牌中添加额外信息。这时,我们可以使用协议映射器来实现。
本教程将展示如何向Keycloak授权服务器添加自定义协议映射器。
2. 协议映射器
Keycloak令牌只是一个包含一组声明的JSON对象,并通常进行数字签名。让我们查看一个令牌负载及其标准化声明集合的例子:
{
"exp": 1680679982,
"iat": 1680679682,
"jti": "bebf7b2c-f813-47be-ad63-0ca6323bba19",
"iss": "http://192.168.198.128:8085/auth/realms/baeldung",
"aud": "account",
"sub": "648b0687-c002-441d-b797-0003b30168ed",
"typ": "Bearer",
"azp": "client-app",
"acr": "1",
...
"scope": "email profile",
"clientId": "client-app",
"clientHost": "192.168.198.1",
"email_verified": false,
"preferred_username": "service-account-client-app",
"clientAddress": "192.168.198.1"
}
协议映射器将电子邮件地址等项目映射到身份和访问令牌中的特定声明。我们可以通过为客户端添加协议映射器来定制令牌中的声明,提供具体细节。
3. 设置Keycloak服务器
在这个教程中,我们将使用独立部署的Keycloak版本。关于如何设置Keycloak服务器的详细步骤已在此处介绍,这里不再赘述。
让我们在Keycloak实例中添加一个新的领域(realm)称为 baeldung 和一个新的客户端名为 client-app:
除了 客户端身份验证 和 服务帐户角色 字段,我们将保留默认设置:
服务帐户角色 字段启用此客户端对客户端凭据许可的支持。
4. 自定义协议映射器实现
现在,我们已经设置了Keycloak服务器,接下来将创建一个自定义协议映射器并在Keycloak服务器中配置它。
4.1. 依赖项
我们的自定义协议映射器是一个常规的Maven项目,生成JAR文件。
首先,在项目的pom.xml
中声明依赖项,包括keycloak-core、keycloak-server-spi、keycloak-server-spi-private和keycloak-services:
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<scope>provided</scope>
<version>21.0.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<scope>provided</scope>
<version>21.0.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<scope>provided</scope>
<version>21.0.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<scope>provided</scope>
<version>21.0.1</version>
</dependency>
4.2. 扩展AbstractOIDCProtocolMapper
类
现在,我们将创建我们的协议映射器。为此,我们继承AbstractOIDCProtocolMapper
类并实现所有抽象方法:
public class CustomProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper,
OIDCIDTokenMapper, UserInfoTokenMapper {
public static final String PROVIDER_ID = "custom-protocol-mapper";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
static {
OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties);
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, CustomProtocolMapper.class);
}
@Override
public String getDisplayCategory() {
return "Token Mapper";
}
@Override
public String getDisplayType() {
return "Custom Token Mapper";
}
@Override
public String getHelpText() {
return "Adds a Baeldung text to the claim";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel,
UserSessionModel userSession, KeycloakSession keycloakSession,
ClientSessionContext clientSessionCtx) {
OIDCAttributeMapperHelper.mapClaim(token, mappingModel, "Baeldung");
}
}
我们选择的提供商ID是 “custom-protocol-mapper”,这是令牌映射器的ID。我们需要这个ID来在Keycloak服务器中配置协议映射器。
**主要的方法是setClaim()
**。它将我们的数据添加到令牌中。在setClaim()
实现中,我们简单地在令牌中添加了一个 Baeldung 文本。
getDisplayType()
和getHelpText()
方法用于管理员控制台。getDisplayType()
方法定义了在列出协议映射器时将在管理员控制台显示的文本。getHelpText()
方法是选择协议映射器时显示的帮助提示文本。
4.3. 整合所有内容
别忘了创建服务定义文件,并将其添加到项目中。文件应命名为org.keycloak.protocol.ProtocolMapper
,并放置在最终JAR的META-INF/services
目录中。文件内容应为自定义协议映射器实现的完全限定类名:
com.baeldung.auth.provider.mapper.CustomProtocolMapper
项目目录结构如下:
现在,项目可以运行了。首先,我们使用Maven的install
命令创建JAR文件:
mvn clean install
接下来,我们将JAR文件部署到Keycloak,将其添加到providers目录中。然后,我们需要重启服务器以更新服务器的提供程序注册表,以便使用JAR文件中的实现:
$ bin/kc.sh start-dev
如控制台输出所示,Keycloak已注册我们的自定义协议映射器:
Updating the configuration and installing your custom providers, if any. Please wait.
2023-04-05 14:55:42,588 WARN [org.keycloak.services] (build-108) KC-SERVICES0047: custom-protocol-mapper (com.baeldung.auth.provider.CustomProtocolMapper) is implementing the internal SPI protocol-mapper.
最后,如果我们前往Keycloak的管理员控制台的Provider info页面,我们会看到我们的 custom-protocol-mapper:
现在,我们可以配置服务器使用我们的自定义协议映射器。
4.4. 配置客户端
在Keycloak中,我们可以通过管理员面板添加自定义声明。为此,我们需要转到管理员控制台上的客户端。之前我们创建了一个名为 client-app 的客户端。之后,我们导航到客户端范围选项卡:
现在,点击 client-app-dedicated,然后进入其通过配置添加映射,创建新的映射:
在这里,我们需要输入映射器类型,这是我们自定义映射器的值。我们将输入“Custom Token Mapper”,这与我们在CustomProtocolMapper
类的getDisplayType()
方法中使用的值相同:
接下来,给映射器命名并保存。回到client-app-dedicated时,我们会看到列表中的新映射:
现在,我们准备好测试我们的协议映射器。
5. 测试
让我们使用客户端凭据许可类型为客户端获取访问令牌:
curl --location --request POST 'http://server-ip:server-port/auth/realms/baeldung/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=client-app' \
--data-urlencode 'client_secret=<client-secret>' \
--data-urlencode 'grant_type=client_credentials'
如果我们获取一个访问令牌并使用jwt.io解码,我们会在令牌体中找到 test 声明:
{
"exp": 1680679982,
"iat": 1680679682,
...
"email_verified": false,
"test": "Baeldung",
"preferred_username": "service-account-client-app",
"clientAddress": "192.168.198.1"
}
如我们所见,test 声明的值为 Baeldung。
6. 总结
在这篇文章中,我们在Keycloak服务器中实现了自定义协议映射器。一般来说,令牌是一组属性或声明。协议映射器提供了将自定义声明添加到令牌中的选项。
本文中展示的代码的完整工作版本可以在GitHub上找到。