服务器开发人员指南
使开源包含更多 复制链接链接已复制到粘贴板!
红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始:master、slave、黑名单和白名单。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。有关更多详情,请参阅我们的首席技术官 Chris Wright 提供的消息。
第 1 章 前言 复制链接链接已复制到粘贴板!
在一些示例列表中,在一行中显示什么内容不适用于可用的页面宽度。这些行已被分类。行末尾的 '\' 表示在页面中引入了一个中断,其中包含以下行缩进。因此:
Let's pretend to have an extremely \ long line that \ does not fit This one is short
Let's pretend to have an extremely \
long line that \
does not fit
This one is short
真正:
Let's pretend to have an extremely long line that does not fit This one is short
Let's pretend to have an extremely long line that does not fit
This one is short
第 2 章 admin REST API 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 附带了一个功能齐全的 Admin REST API,以及管理控制台提供的所有功能。
若要调用 API,您需要获取具有适当权限的访问令牌。服务器管理指南 中描述了所需的权限。
您可以使用红帽构建的 Keycloak 为应用程序启用身份验证来获取令牌;请参阅 保护应用程序和服务指南。您还可以使用直接访问授权来获取访问令牌。
2.1. 使用 CURL 的示例 复制链接链接已复制到粘贴板!
2.1.1. 使用用户名和密码进行身份验证 复制链接链接已复制到粘贴板!
以下示例假设您使用 master 域中的 密码 创建了 admin 用户,如 Getting Started Guide 指南 所示。
流程
使用用户名
admin和密码,获取 realmmaster中用户的访问令牌:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意默认情况下,这个令牌在 1 分钟内过期
结果将是 JSON 文档。
-
通过提取
access_token属性的值来调用所需的 API。 通过对 API 的请求的
Authorization标头中包含值来调用 API。以下示例演示了如何获取 master 域的详情:
curl \ -H "Authorization: bearer eyJhbGciOiJSUz..." \ "http://localhost:8080/admin/realms/master"
curl \ -H "Authorization: bearer eyJhbGciOiJSUz..." \ "http://localhost:8080/admin/realms/master"Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.1.2. 使用服务帐户进行身份验证 复制链接链接已复制到粘贴板!
要使用 client_id 和 client_secret 对 Admin REST API 进行身份验证,请执行此流程。
流程
确保客户端已配置如下:
-
client_id是 属于 realm master的机密客户端 -
client_id启用了Service Accounts Enabled选项 client_id有一个自定义"Audience"映射程序-
Includes Client Audience:
security-admin-console
-
Includes Client Audience:
-
-
检查
client_id是否在"服务帐户角色"选项卡中分配了角色"admin"。
curl \ -d "client_id=<YOUR_CLIENT_ID>" \ -d "client_secret=<YOUR_CLIENT_SECRET>" \ -d "grant_type=client_credentials" \ "http://localhost:8080/realms/master/protocol/openid-connect/token"
curl \
-d "client_id=<YOUR_CLIENT_ID>" \
-d "client_secret=<YOUR_CLIENT_SECRET>" \
-d "grant_type=client_credentials" \
"http://localhost:8080/realms/master/protocol/openid-connect/token"
2.2. 其他资源 复制链接链接已复制到粘贴板!
第 3 章 themes 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 为网页和电子邮件提供主题支持。这允许自定义面向最终用户的页面的外观和感觉,以便它们可以与您的应用程序集成。
图 3.1. 带有模糊示例主题的登录页面
3.1. 主题类型 复制链接链接已复制到粘贴板!
主题可以提供一个或多个类型来自定义红帽构建的 Keycloak 的不同方面。可用的类型有:
- 帐户 - 帐户管理
- Admin - Admin Console
- 电子邮件 - 电子邮件
- 登录 - 登录表单
- welcome - Welcome page
3.2. 配置主题 复制链接链接已复制到粘贴板!
除 welcome 外,所有主题类型都通过管理控制台进行配置。
流程
- 登录管理控制台。
- 从左上角的下拉菜单中选择您的域。
- 从菜单中选择 Realm Settings。
点 Themes 选项卡。
注意要为
master管理控制台设置主题,您需要为master域设置 Admin Console 主题。- 要查看 Admin Console 刷新页面的更改。
-
使用
spi-theme-welcome-theme选项更改欢迎主题。 例如:
bin/kc.[sh|bat] start --spi-theme-welcome-theme=custom-theme
bin/kc.[sh|bat] start --spi-theme-welcome-theme=custom-themeCopy to Clipboard Copied! Toggle word wrap Toggle overflow
3.3. 默认主题 复制链接链接已复制到粘贴板!
红帽 Keycloak 的构建与服务器分发中的 JAR 文件 keycloak-themes-22.0.13.redhat-00001.jar 中的默认主题捆绑在一起。默认情况下,服务器的根目录会包含任何主题,但它包含一个 README 文件,其中包含一些有关默认主题的详情。要简化升级,请不要直接编辑捆绑它们。相反,创建自己的主题来扩展其中一个捆绑的主题。
3.4. 创建主题 复制链接链接已复制到粘贴板!
主题包括:
- HTML 模板(自由标记模板)
- 镜像
- 消息捆绑包
- 风格表
- 脚本
- 主题属性
除非计划替换每个单一页面,否则您应该扩展另一个主题。最有可能要扩展红帽构建的 Keycloak 主题,但如果您显著改变页面的外观和感觉,您也可以考虑扩展基础主题。该主题主要由 HTML 模板和消息捆绑包组成,而红帽构建的 Keycloak 主题主要包含镜像和风格表。
在扩展主题时,您可以覆盖单个资源(templates、样式表等)。如果您决定覆盖 HTML 模板,则可能需要在升级到新版本时更新自定义模板。
虽然创建一个主题,最好不要从 themes 目录中编辑主题资源,而无需重启红帽构建的 Keycloak。
流程
使用以下选项运行 Keycloak:
bin/kc.[sh|bat] start --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false
bin/kc.[sh|bat] start --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=falseCopy to Clipboard Copied! Toggle word wrap Toggle overflow 在
themes目录中创建一个目录。目录的名称成为主题的名称。例如,要创建一个名为
mytheme的主题,请创建目录themes/mytheme。在主题目录中,为您的主题提供的每种类型创建一个目录。
例如,要将登录类型添加到
mytheme主题,请创建themes/mytheme/login目录。对于每个类型,创建一个文件
theme.properties,允许为主题设置一些配置。例如,若要配置主题
themes/mytheme/login来扩展基础主题并导入一些常见资源,请使用以下内容创建文件themes/mytheme/login/theme.properties:parent=base import=common/keycloak
parent=base import=common/keycloakCopy to Clipboard Copied! Toggle word wrap Toggle overflow 现在,您已创建了一个支持登录类型的主题。
- 登录管理控制台以签出您的新主题
- 选择您的域
- 从菜单中选择 Realm Settings。
- 点 Themes 选项卡。
- 对于 Login Theme,请选择 mytheme 并点 Save。
打开域的登录页面。
您可以通过应用程序登录,或者打开帐户管理控制台(
/realms/{realm name}/account)来完成此操作。-
要查看更改父主题的影响,请在me
.properties中设置parent=keycloak并刷新登录页面。
务必在生产环境中重新启用缓存,因为它会对性能有严重影响。
如果要手动删除主题缓存的内容,您可以通过删除服务器发行版本的 data/tmp/kc-gzip-cache 目录来实现。如果您重新部署了自定义提供程序或自定义它们而不在之前的服务器执行中禁用它们缓存,则它很有用。
3.4.1. 主题属性 复制链接链接已复制到粘贴板!
me 属性在主题目录中的 < ;THEME TYPE>/theme.properties 文件中设置。
- Parent - 要扩展的父主题
- import - 从另一个主题导入资源
-
common - 覆盖通用资源路径。如果没有指定,默认值为
common/keycloak。这个值将用作${url.resourcesCommonPath}的后缀值,它通常用于 freemarker 模板(${url.resoucesCommonPath}的值为主题 root uri)。 - 风格 - 要包括的空间分开的样式列表,包括
- locales - 以逗号分隔的支持的区域列表
有可用于更改某些元素类型的 css 类的属性列表。有关这些属性的列表,请查看 keycloak 主题的相应类型的me.properties 文件(themes/keycloak/<THEME TYPE>/theme.properties)。
您还可以添加自己的自定义属性,并从自定义模板中使用它们。
当这样做时,您可以使用以下格式替换系统属性或环境变量:
-
${some.system.property}- for system properties -
${env.ENV_VAR}- 用于环境变量。
如果系统属性或环境变量没有使用 ${foo:defaultValue} 找到,也可以提供默认值。
如果没有提供默认值,且没有对应的系统属性或环境变量,则不会替换任何内容,并在模板中以格式结尾。
以下是可能的内容示例:
javaVersion=${java.version}
unixHome=${env.HOME:Unix home not found}
windowsHome=${env.HOMEPATH:Windows home not found}
javaVersion=${java.version}
unixHome=${env.HOME:Unix home not found}
windowsHome=${env.HOMEPATH:Windows home not found}
3.4.2. 在主题中添加风格表 复制链接链接已复制到粘贴板!
您可以将一个或多个风格表添加到主题。
流程
-
在主题的 <
;THEME TYPE>/resources/css目录中创建一个文件。 将此文件添加到me
.properties 中的属性中。styles例如,要在
mytheme中添加styles.css,请使用以下内容创建themes/mytheme/login/resources/css/styles.css:.login-pf body { background: DimGrey none; }.login-pf body { background: DimGrey none; }Copy to Clipboard Copied! Toggle word wrap Toggle overflow 编辑
themes/mytheme/login/theme.properties并添加:styles=css/styles.css
styles=css/styles.cssCopy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看更改,请打开域的登录页面。
您会注意到,唯一应用风格的样式是自定义风格表中的。
要包括父主题的样式,请从该主题加载样式。编辑
themes/mytheme/login/theme.properties并将风格改为:styles=web_modules/@fontawesome/fontawesome-free/css/icons/all.css web_modules/@patternfly/react-core/dist/styles/base.css web_modules/@patternfly/react-core/dist/styles/app.css node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css css/login.css css/styles.css
styles=web_modules/@fontawesome/fontawesome-free/css/icons/all.css web_modules/@patternfly/react-core/dist/styles/base.css web_modules/@patternfly/react-core/dist/styles/app.css node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css css/login.css css/styles.cssCopy to Clipboard Copied! Toggle word wrap Toggle overflow 注意要覆盖父风格表的样式,请确保最后列出您的样式表。
3.4.3. 在主题中添加脚本 复制链接链接已复制到粘贴板!
您可以在主题中添加一个或多个脚本。
流程
-
在主题的 <
;THEME TYPE>/resources/js目录中创建一个文件。 将文件添加到me
.properties 中的属性中。scripts例如,要将
script.js添加到mytheme,请使用以下内容创建themes/mytheme/login/resources/js/script.js:alert('Hello');alert('Hello');Copy to Clipboard Copied! Toggle word wrap Toggle overflow 然后编辑
themes/mytheme/login/theme.properties并添加:scripts=js/script.js
scripts=js/script.jsCopy to Clipboard Copied! Toggle word wrap Toggle overflow
3.4.4. 将镜像添加到主题 复制链接链接已复制到粘贴板!
要使镜像可供主题使用,将它们添加到主题的 < ;THEME TYPE>/resources/img 目录中。它们可用于样式表,也可以在 HTML 模板中直接使用。
例如,要将镜像添加到 mytheme 中,将镜像复制到 themes/mytheme/login/resources/img/image.jpg 中。
然后您可以在自定义风格的表格中使用此镜像:
body {
background-image: url('../img/image.jpg');
background-size: cover;
}
body {
background-image: url('../img/image.jpg');
background-size: cover;
}
或者直接在 HTML 模板中使用,将以下内容添加到自定义 HTML 模板中:
<img src="${url.resourcesPath}/img/image.jpg">
<img src="${url.resourcesPath}/img/image.jpg">
3.4.5. messages 复制链接链接已复制到粘贴板!
模板中的文本是从消息捆绑包加载的。扩展另一个主题的主题将继承父消息捆绑包中的所有消息,您可以通过将 < THEME TYPE>/messages/messages_en.properties 添加到您的主题来覆盖单个消息。
例如,要将登录表单上的 Username 替换为 mytheme 的 your Username,将文件 themes/mytheme/login/messages/messages_en.properties 替换为以下内容:
usernameOrEmail=Your Username
usernameOrEmail=Your Username
在使用消息时,在消息值中,{0} 和 IANA 等消息值将被替换为参数。例如,登录到 {0} 中的 {0} 被替换为域的名称。
这些消息捆绑包的文本可以被特定于域的值覆盖。特定于域的值可以通过 UI 和 API 进行管理。
3.4.6. 在域中添加语言 复制链接链接已复制到粘贴板!
先决条件
- 要为域启用国际化,请参阅 服务器管理指南。
流程
-
在主题的目录中创建文件
<THEME TYPE>/messages/messages_<LOCALE>.properties。 将此文件添加到 <
THEME TYPE>/theme.properties中的区域设置属性中。对于可供域登录、帐户和电子邮件使用的语言,主题必须支持语言,因此您需要为这些主题类型添加您的语言。例如,要将 Norwegian 翻译添加到
mytheme主题中,创建包含以下内容的文件 themes/mytheme/login/messages_no.properties:usernameOrEmail=Brukernavn password=Passord
usernameOrEmail=Brukernavn password=PassordCopy to Clipboard Copied! Toggle word wrap Toggle overflow 如果您省略了消息的翻译,将使用英语。
编辑
themes/mytheme/login/theme.properties并添加:locales=en,no
locales=en,noCopy to Clipboard Copied! Toggle word wrap Toggle overflow -
为
account和email主题类型添加相同的内容。为此,请创建themes/mytheme/account/messages/messages_no.properties和themes/mytheme/email/messages/messages_no.properties。将这些文件留空将导致使用英语消息。 -
将
themes/mytheme/login/theme.properties复制到themes/mytheme/account/theme.properties和themes/mytheme/email/theme.properties。 为语言选择器添加翻译。这可以通过向英语添加消息来完成。要做到这一点,将以下内容添加到
themes/mytheme/account/messages_en.properties中,以及themes/mytheme/login/messages_en.properties:locale_no=Norsk
locale_no=NorskCopy to Clipboard Copied! Toggle word wrap Toggle overflow 默认情况下,消息属性文件应该使用 ISO-8859-1 进行编码。也可以使用特殊标头指定编码。例如,使用 UTF-8 编码:
encoding: UTF-8 usernameOrEmail=....
# encoding: UTF-8 usernameOrEmail=....Copy to Clipboard Copied! Toggle word wrap Toggle overflow
3.4.7. 添加自定义身份提供程序图标 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 支持为自定义身份提供程序添加图标,这些图标显示在登录屏幕中。
流程
-
在您的登录
主题.properties文件中定义图标类(例如:themes/mytheme/login/theme.properties),带有键模式kcLogoIdP-<alias>。 对于别名为
myProvider的身份提供程序,您可以在自定义主题的me.properties文件中添加一行。例如:kcLogoIdP-myProvider = fa fa-lock
kcLogoIdP-myProvider = fa fa-lockCopy to Clipboard Copied! Toggle word wrap Toggle overflow
所有图标都位于 PatternFly4 的官方网站上。社交供应商的图标已在基础登录主题属性(themes/keycloak/login/theme.properties)中定义,您可以在其中提升自己。
3.4.8. 创建自定义 HTML 模板 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 使用 Apache Freemarker 模板来生成 HTML。您可以通过创建 < THEME TYPE>/<TEMPLATE>.ftl 来覆盖您自己的主题中的单个模板。有关使用的模板列表请查看 themes/base/<THEME TYPE>。
流程
- 将模板从基础主题复制到您自己的主题。
应用您需要的修改。
例如,要为
mytheme主题创建自定义登录表单,请将themes/base/login/login.ftl复制到themes/mytheme/login并在编辑器中打开它。在第一行(<114import …>)后,添加
<h1>HELLO WORLD!</h1>,如下所示:<#import "template.ftl" as layout> <h1>HELLO WORLD!</h1> ...
<#import "template.ftl" as layout> <h1>HELLO WORLD!</h1> ...Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 备份修改后的模板。当升级到新版本的 Keycloak 时,您可能需要更新您的自定义模板,以便在适用时对原始模板应用更改。
3.4.9. 电子邮件 复制链接链接已复制到粘贴板!
要编辑电子邮件的主题和内容,如密码恢复电子邮件,请在主题 的电子邮件 类型中添加消息捆绑包。每个电子邮件都有三个消息:一个主题,一个用于纯文本正文,一个用于 html 正文。
要查看所有可用的电子邮件,请查看 themes/base/email/messages/messages_en.properties。
例如,要更改 mytheme 主题的密码恢复电子邮件,创建 themes/mytheme/email/messages/messages_en.properties,其内容如下:
passwordResetSubject=My password recovery
passwordResetBody=Reset password link: {0}
passwordResetBodyHtml=<a href="{0}">Reset password</a>
passwordResetSubject=My password recovery
passwordResetBody=Reset password link: {0}
passwordResetBodyHtml=<a href="{0}">Reset password</a>
3.5. 部署主题 复制链接链接已复制到粘贴板!
主题目录可以部署到 Keycloak 的红帽构建中,方法是将主题目录复制到 主题 目录,也可以部署为存档。在开发过程中,您可以将主题复制到 主题 目录中,但在生产环境中,您可能想考虑使用 存档。通过 存档,可以更轻松地显示主题的版本控制副本,特别是当您有多个红帽构建的 Keycloak 实例(例如用于集群)时。
流程
- 要将主题部署为存档,请创建一个带有主题资源的 JAR 存档。
在存档中添加文件
META-INF/keycloak-themes.json,该文件在存档中列出可用主题,以及每个主题提供的类型。例如,对于
mythemetheme createmytheme.jar,其内容如下:- META-INF/keycloak-themes.json
- theme/mytheme/login/theme.properties
- theme/mytheme/login/login.ftl
- theme/mytheme/login/resources/css/styles.css
- theme/mytheme/login/resources/img/image.png
- theme/mytheme/login/messages/messages_en.properties
theme/mytheme/email/messages/messages_en.properties
本例中的
META-INF/keycloak-themes.json的内容将是:Copy to Clipboard Copied! Toggle word wrap Toggle overflow 单个存档可以包含多个主题,每个主题可以支持一个或多个类型。
要将存档部署到红帽构建的 Keycloak 中,请将其添加到红帽构建的 Keycloak 的 providers/ 目录中,并在服务器已在运行时重新启动服务器。
3.7. 主题资源 复制链接链接已复制到粘贴板!
在 Red Hat build of Keycloak 中实施自定义供应商时,可能需要添加额外的模板、资源和消息捆绑包。
加载其他主题资源的最简单方法是在me-resources/resources 中的me -resources/templates 资源中 创建带有 模板的 JAR,并在me 中带有消息捆绑包。
-resources/ messages
如果您希望一种更灵活的方法来加载可通过 ThemeResourceSPI 实现的模板和资源。通过实施 ThemeResourceProviderFactory 和 ThemeResourceProvider Provider,您可以精确决定如何加载模板和资源。
3.8. Local selector 复制链接链接已复制到粘贴板!
默认情况下,使用 DefaultLocaleSelectorProvider 选择区域设置,它实现了 LocaleSelectorProvider 接口。英语是禁用国际化时的默认语言。
启用国际化后,区域设置会根据 服务器管理指南 中描述的逻辑来解决。
通过实施 LocaleSelectorProvider 和 LocaleSelectorProviderFactory,可通过 LocaleSelectorSPI 更改此行为。
LocaleSelectorProvider 接口有一个单一方法 resolveLocale,它必须返回给定一个 RealmModel 和 nullable UserModel 的区域。实际请求可从 KeycloakSession114getContext 方法获得。
自定义实施可以扩展 DefaultLocaleSelectorProvider,以重复使用默认行为的部分。例如,要忽略 Accept-Language 请求标头,自定义实施可以扩展默认提供程序,覆盖它的 getAcceptLanguageHeaderLocale,并返回 null 值。因此,区域设置选择将会回退到域的默认语言。
第 4 章 自定义用户属性 复制链接链接已复制到粘贴板!
您可以使用自定义主题在注册页面中添加自定义用户属性。
4.1. 注册页面 复制链接链接已复制到粘贴板!
使用这个流程在注册页面中输入自定义属性。
流程
-
将模板
themes/base/login/register.ftl复制到自定义主题的登录类型。 在编辑器中打开副本。
例如,要在注册页面中添加移动号,请在表单中添加以下代码片段:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
确保输入 html 元素的名称以
user.attributes开头。在上例中,属性将由红帽构建的 Keycloak 存储,名称为mobile。 - 要查看更改,请确保您的域使用您的自定义主题作为登录主题,并打开注册页面。
第 5 章 Identity Brokering API 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 可将身份验证委托给父 IDP 以进行登录。其中一个典型的示例是您希望用户能够通过社交供应商(如 Ice 或 Google)登录。您还可以将现有帐户链接到代理的 IDP。本节论述了应用程序可以使用的一些 API 作为身份代理相关。
5.1. 检索外部 IDP 令牌 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 允许您使用外部 IDP 存储来自身份验证流程的令牌和响应。为此,您可以在 IDP 的设置页面中使用 Store Token 配置选项。
应用程序代码可以检索这些令牌和响应来拉取额外的用户信息,或者安全地在外部 IDP 上调用请求。例如,应用程序可能希望使用 Google 令牌在其他 Google 服务和 REST API 上调用。要检索特定身份提供程序的令牌,您需要发送请求,如下所示:
GET /realms/{realm}/broker/{provider_alias}/token HTTP/1.1
Host: localhost:8080
Authorization: Bearer <KEYCLOAK ACCESS TOKEN>
GET /realms/{realm}/broker/{provider_alias}/token HTTP/1.1
Host: localhost:8080
Authorization: Bearer <KEYCLOAK ACCESS TOKEN>
应用程序必须通过红帽构建的 Keycloak 进行身份验证,并收到访问令牌。此访问令牌需要设置 代理 客户端级别角色 read-token。这意味着用户必须具有此角色的角色映射,并且客户端应用程序必须在其范围内拥有该角色。在这种情况下,由于您在红帽构建的 Keycloak 中访问受保护的服务,您需要在用户身份验证过程中发送由红帽构建 Keycloak 发布的访问令牌。在代理配置页面中,您可以通过打开 Stored Tokens Readable 开关,自动将此角色分配给新导入的用户。
通过再次通过提供程序登录或使用发起的帐户链接 API,可以重新建立这些外部令牌。
5.2. 客户端发起的帐户链接 复制链接链接已复制到粘贴板!
有些应用程序需要与实验室供应商(如 Ice)集成,但不想提供通过这些社交提供商登录的选项。红帽构建的 Keycloak 提供了一个基于浏览器的 API,应用程序可以使用它将现有用户帐户链接到特定的外部 IDP。这称为 client-initiated account link。链接的帐户只能由 OIDC 应用程序启动。
其工作方式在于,应用程序将用户的浏览器转发到红帽构建的 Keycloak 服务器上的 URL,要求它希望将用户帐户链接到特定的外部提供程序(例如,Facebook)。服务器使用外部提供程序启动登录。浏览器在外部提供程序登录,并重定向到服务器。服务器建立链接,并通过确认重新重定向到应用程序。
在启动此协议前,客户端应用程序必须满足一些先决条件:
- 必须为 admin 控制台中的用户域配置和启用所需的身份提供程序。
- 用户帐户必须已经通过 OIDC 协议以现有用户身份登录
-
用户必须具有
account.manage-account或account.manage-account-links角色映射。 - 应用程序必须在其访问令牌内授予这些角色的范围
- 应用必须有权访问其访问令牌,因为它需要其中的信息来生成重定向 URL。
要启动登录,应用程序必须光纤一个 URL,并将用户的浏览器重定向到此 URL。URL 类似如下:
/{auth-server-root}/realms/{realm}/broker/{provider}/link?client_id={id}&redirect_uri={uri}&nonce={nonce}&hash={hash}
/{auth-server-root}/realms/{realm}/broker/{provider}/link?client_id={id}&redirect_uri={uri}&nonce={nonce}&hash={hash}
下面是每个路径和查询参数的描述:
- provider
-
这是您在管理控制台的
Identity Provider部分中定义的外部 IDP 的供应商别名。 - client_id
- 这是应用程序的 OIDC 客户端 ID。当您在 admin 控制台中将应用程序注册为客户端时,您必须指定此客户端 ID。
- redirect_uri
- 这是您在建立帐户链接后要重定向到的应用程序回调 URL。它必须是有效的客户端重定向 URI 模式。换句话说,它必须与您在管理控制台中注册客户端时定义的一个有效 URL 模式匹配。
- nonce
- 这是应用程序必须生成的随机字符串
- hash
-
这是一个 Base64 URL 编码哈希。此哈希值由 Base64 URL 编码的
一个非ce+token.getSessionState ()+token.getIssuedFor ()的 SHA_256 哈希生成。令牌变量从 OIDC 访问令牌获取。基本上,您要对随机非ce、用户会话 ID、客户端 ID 和您要访问的身份提供程序别名进行哈希处理。
以下是生成 URL 的 Java Servlet 代码示例,以建立帐户链接。
为什么包含这个哈希?我们这样做,以便身份验证服务器可以保证知道客户端应用程序启动请求,且没有其他恶意应用程序只是随机要求用户帐户链接到特定提供程序。身份验证服务器首先将检查用户是否登录,方法是检查登录时设置的 SSO cookie。然后,它将尝试基于当前登录重新生成哈希,并将它与应用发送的哈希值匹配。
链接帐户后,auth 服务器将重定向到 redirect_uri。如果提供链接请求的问题,auth 服务器可能会也可能不会重定向到 redirect_uri。浏览器可能只是在错误页面结束,而不是重定向到应用。如果出现错误条件,并且 auth 服务器认为能够安全地重定向到客户端应用,则将附加额外的 错误 查询参数到 redirect_uri。
虽然此 API 保证应用程序启动请求,但它不会完全防止 CSRF 攻击。应用程序仍负责保护 CSRF 攻击目标。
5.2.1. 刷新外部令牌 复制链接链接已复制到粘贴板!
如果您使用登录到供应商生成的外部令牌(例如,Cyter 或 GitHub 令牌),您可以通过重新发起帐户链接 API 来刷新此令牌。
第 6 章 服务提供商接口(SPI) 复制链接链接已复制到粘贴板!
Red Hat build of Keycloak 旨在覆盖大多数用例,而无需自定义代码,但我们也希望它可以被自定义。为了实现此红帽构建的 Keycloak,有许多服务提供商接口(SPI),您可以自行实施供应商。
6.1. 实施 SPI 复制链接链接已复制到粘贴板!
要实施 SPI,您需要实施其 ProviderFactory 和 Provider 接口。您还需要创建服务配置文件。
例如,要实施 Theme Selector SPI,您需要实施 ThemeSelectorProviderFactory 和 ThemeSelectorProviderFactory,并提供文件 META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory。
ThemeSelectorProviderFactory 示例:
建议您的供应商工厂实现通过方法 getId () 返回唯一 id。但是,在 Overriding providers 部分所述,这个规则可能存在一些例外。
红帽构建的 Keycloak 创建一个供应商工厂实例,以便可以存储多个请求的状态。通过调用每个请求的工厂的 create 来创建提供程序实例,以便这些实例应轻量级对象。
ThemeSelectorProvider 示例:
服务配置文件示例(META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory):
org.acme.provider.MyThemeSelectorProviderFactory
org.acme.provider.MyThemeSelectorProviderFactory
要配置您的提供程序,请参阅配置提供程序 章节。
例如,要配置供应商,您可以按如下所示设置选项:
bin/kc.[sh|bat] --spi-theme-selector-my-theme-selector-enabled=true --spi-theme-selector-my-theme-selector-theme=my-theme
bin/kc.[sh|bat] --spi-theme-selector-my-theme-selector-enabled=true --spi-theme-selector-my-theme-selector-theme=my-theme
然后,您可以使用 ProviderFactory init 方法检索配置:
public void init(Config.Scope config) {
String themeName = config.get("theme");
}
public void init(Config.Scope config) {
String themeName = config.get("theme");
}
如果需要,您的供应商也可以查找其他供应商。例如:
6.1.1. 覆盖内置供应商 复制链接链接已复制到粘贴板!
如前文所述,建议您使用 ProviderFactory 实现的唯一 ID。但是,同时,覆盖红帽构建的 Keycloak 内置供应商会很有用。建议的做法是使用唯一 ID 的 ProviderFactory 实施,然后实例用于设置 配置提供程序一章中所述的默认提供程序。另一方面,这可能无法始终实现。
例如,当您需要对默认 OpenID Connect 协议进行一些自定义,并希望覆盖默认的 OIDCLoginProtocolFactory 实现的 Keycloak 实现时,您需要保留相同的 providerId。例如,OIDC 协议众所周知的端点,各种因素依赖于开放连接协议工厂的 ID。
在这种情况下,强烈建议您实施自定义实施的方法 order (),并确保其顺序高于内置实施。
如果有多个具有相同供应商 ID 的实现,则红帽构建 Keycloak 运行时只会使用具有最高顺序的那一个。
6.1.2. 在管理门户中显示来自您的 SPI 实现的信息 复制链接链接已复制到粘贴板!
有时,向红帽构建的 Keycloak 管理员显示有关供应商的额外信息会很有用。您可以显示供应商构建时间信息(例如,当前安装的自定义供应商版本)、供应商的当前配置(如您的供应商与远程系统通信的 url)或一些操作信息(来自您供应商的远程系统的响应时间)。红帽构建的 Keycloak 管理控制台提供了 Server Info 页面来显示此类信息。
要显示来自您的提供程序的信息,可以在您的 ProviderFactory 中实施 org.keycloak.provider.ServerInfoAware 接口。
ProviderFactory
上例中的 MyThemeSelectorProviderFactory 的实现示例:
6.2. 使用可用供应商 复制链接链接已复制到粘贴板!
在供应商实现中,您可以使用 Red Hat build of Keycloak 中提供的其他供应商。现有提供程序通常使用 KeycloakSession 来检索,该提供程序可供您的供应商使用,如 实施 SPI 一节中所述。
红帽构建的 Keycloak 有两个供应商类型:
单实施供应商类型 - 红帽构建的 Keycloak 运行时中只能有一个活跃的特定供应商类型实现。
例如
HostnameProvider指定红帽构建的 Keycloak 使用的主机名,并为整个红帽构建的 Keycloak 服务器共享。因此,对于红帽构建的 Keycloak 服务器,只能有一个有效的供应商实现。如果服务器运行时有多个提供程序实现,则需要将其指定为默认值。
例如:
bin/kc.[sh|bat] build --spi-hostname-provider=default
bin/kc.[sh|bat] build --spi-hostname-provider=default
用作 值的默认值必须与特定提供程序工厂实施的 default -providerProviderFactory.getId () 返回的 ID 匹配。在代码中,您可以获取提供程序,如 keycloakSession.getProvider (HostnameProvider.class)
多个实现供应商类型 - Those 是供应商类型,它允许在红帽构建的 Keycloak 运行时中有多个实施并协同工作。
例如,
EventListener供应商允许有多个可用的实现并注册,这意味着特定的事件可以发送到所有监听器(jboss-logging、sysout 等)。在代码中,您可以获取供应商的指定实例,如session.getProvider (EventListener.class, "jboss-logging")。您需要将供应商的provider_id指定为第二个参数,因为上面所述,该供应商类型有多个实例。供应商 ID 必须与特定供应商工厂实施的
ProviderFactory.getId ()返回的 ID 匹配。某些提供程序类型可以通过使用ComponentModel作为第二个参数来检索,即使需要使用KeycloakSessionFactory来检索一些提供程序(例如Authenticator)。不建议以这种方式实施您自己的供应商,因为它将来可能被弃用。
6.3. 注册供应商实施 复制链接链接已复制到粘贴板!
通过将提供程序复制到供应商目录中,即可将其注册到服务器。
如果您的供应商需要额外的依赖项,且 Keycloak 尚未提供,请将这些依赖项复制到 provider 目录中。
在注册新供应商或依赖项 Keycloak 后,需要使用 kc.[sh|bat] build 命令重新构建。
6.3.1. 禁用供应商 复制链接链接已复制到粘贴板!
您可以通过将供应商的 enabled 属性设置为 false 来禁用供应商。例如,禁用 Infinispan 用户缓存提供程序使用:
bin/kc.[sh|bat] build --spi-user-cache-infinispan-enabled=false
bin/kc.[sh|bat] build --spi-user-cache-infinispan-enabled=false
6.4. JavaScript 供应商 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 能够在运行时执行脚本,以便管理员能够自定义特定功能:
- 验证器
- JavaScript 策略
- OpenID Connect 协议映射程序
- SAML 协议映射程序
6.4.1. 验证器 复制链接链接已复制到粘贴板!
身份验证脚本必须至少提供以下功能之一: authentication (..),从 Authenticator#authenticate (AuthenticationFlowContext)操作(.. )调用,它从 Authenticator#action (AuthenticationFlowContext)调用。
自定义 Authenticator 应该至少提供身份验证 (...) 功能。您可以在代码中使用 javax.script.Bindings 脚本。
script-
用于访问脚本元数据的
ScriptModel realm-
RealmModel user-
当前
UserModel。请注意,如果在身份验证流中配置了脚本验证器时,用户就可以在另一个验证器成功下触发,并将该用户设置为身份验证会话。 会话-
活跃的
KeycloakSession AuthenticationSession-
当前
身份验证SessionModel httpRequest-
当前的
org.jboss.resteasy.spi.HttpRequest LOG-
org.jboss.logging.Logger限定到scriptsBasedAuthenticator
您可以从传递给 身份验证(context) 信息。
操作( 参数中提取其他上下文context )函数的 context
6.4.1.1. 在什么位置添加脚本验证器 复制链接链接已复制到粘贴板!
可以使用脚本验证器是在身份验证结束时进行一些检查。请注意,如果您希望脚本验证器总是被触发(即使在使用身份 Cookie 的 SSO 重新身份验证过程中为实例),您可能需要将它添加为身份验证流末尾的 REQUIRED,并将现有的验证器封装到单独的 REQUIRED 身份验证子流中。这需要是因为 REQUIRED 和 ALTERNATIVE 执行不应处于相同的级别。例如,身份验证流配置应如下所示:
- User-authentication-subflow REQUIRED -- Cookie ALTERNATIVE -- Identity-provider-redirect ALTERNATIVE ... - Your-Script-Authenticator REQUIRED
- User-authentication-subflow REQUIRED
-- Cookie ALTERNATIVE
-- Identity-provider-redirect ALTERNATIVE
...
- Your-Script-Authenticator REQUIRED
6.4.2. 使用要部署的脚本创建 JAR 复制链接链接已复制到粘贴板!
JAR 文件是带有 .jar 扩展名的常规 ZIP 文件。
为了使您的脚本可供红帽构建的 Keycloak 使用,您需要将它们部署到服务器。为此,您应该创建具有以下结构的 JAR 文件:
META-INF/keycloak-scripts.json my-script-authenticator.js my-script-policy.js my-script-mapper.js
META-INF/keycloak-scripts.json
my-script-authenticator.js
my-script-policy.js
my-script-mapper.js
META-INF/keycloak-scripts.json 是一个文件描述符,提供了有关您要部署的脚本的元数据信息。它是具有以下结构的 JSON 文件:
此文件应该引用您要部署的不同类型的脚本供应商:
验证器对于 OpenID Connect 脚本验证器。您可以在同一 JAR 文件中有一个或者多个验证器
policies对于使用红帽构建的 Keycloak 授权服务时的 JavaScript 策略。您可以在同一个 JAR 文件中具有一个或多个策略
Mappers对于 OpenID Connect 脚本协议映射程序。您可以在同一 JAR 文件中有一个或多个映射程序
saml-mappers对于 SAML 脚本协议映射程序。您可以在同一 JAR 文件中有一个或多个映射程序
对于 JAR 文件中的每个脚本文件,您需要在 META-INF/keycloak-scripts.json 中对应条目,用于将脚本文件映射到特定的供应商类型。为此,您应该为每个条目提供以下属性:
name用于通过红帽构建的 Keycloak 管理控制台显示脚本的友好名称。如果没有提供,将使用脚本文件的名称替代
description更好地描述脚本文件的可选文本
fileName脚本文件的名称。此属性 是必需的,应映射到 JAR 中的文件。
6.4.3. 部署脚本 JAR 复制链接链接已复制到粘贴板!
您有一个带有描述符和您要部署的脚本的 JAR 文件后,您只需要将 JAR 复制到 Keycloak 提供程序/目录的红帽构建中,然后运行 。
bin/ kc.[sh|bat] 构建
6.5. 可用 SPI 复制链接链接已复制到粘贴板!
如果要在运行时查看所有可用的 SPI 列表,您可以在管理门户中检查 Server Info 页面,如 Admin Console 部分所述。
第 7 章 用户存储 SPI 复制链接链接已复制到粘贴板!
此功能取决于 keycloak-model-legacy 和 模块中捆绑的 API。它很快将被替换为新的映射存储 API,它提供了访问用户和其他实体的本地和外部信息的统一方法,并最终删除旧 API。
keycloak-model-legacy -private
您可以使用 User Storage SPI 为红帽构建的 Keycloak 编写扩展,以连接到外部用户数据库和凭证存储。内置的 LDAP 和 sVirt 支持是此操作中此 SPI 的实现。红帽构建的 Keycloak 使用其本地数据库创建、更新和查找用户并验证凭证。虽然组织通常具有现有的外部专有用户数据库,它们无法迁移到红帽构建的 Keycloak 数据模型。对于这些情况,应用程序开发人员可编写 User Storage SPI 的实现,以桥接外部用户存储,以及红帽构建的 Keycloak 的内部用户对象模型,以管理它们。
当红帽构建的 Keycloak 运行时需要查找用户(如用户登录时),它会执行多个步骤来定位用户。首先查看用户是否在用户缓存中;如果找到了该用户,它会使用该内存表示。然后,它会在红帽构建的 Keycloak 本地数据库中查找用户。如果没有找到用户,它将通过 User Storage SPI 供应商实现循环来执行用户查询,直到其中一个用户返回运行时正在查找的用户。供应商查询外部用户存储,并将用户的外部数据表示映射到红帽构建的 Keycloak 用户 metamodel。
用户存储 SPI 提供程序实施也可以对用户执行复杂的条件查询、验证和管理凭证,或者一次性执行许多用户的批量更新。它取决于外部存储的功能。
用户存储 SPI 提供程序实施与(通常)Jakarta EE 组件类似。它们默认不启用,而是必须在管理控制台的 User Federation 选项卡下为每个域启用和配置。
如果您的用户提供程序实施使用某些用户属性作为链接/建立用户身份的元数据属性,请确保用户无法编辑属性,并且对应的属性为只读。示例是 LDAP_ID 属性,内置的红帽构建的 Keycloak LDAP 供应商用于将用户的 ID 存储在 LDAP 服务器端。请参阅 Threat 模型缓解章节中 的详细信息。
红帽构建的 Keycloak Quickstarts 仓库有两个示例项目。每个快速入门都有一个 README 文件,其中包含如何构建、部署和测试示例项目的说明。下表提供了可用用户存储 SPI 快速入门的简短描述:
| Name | 描述 |
|---|---|
| 演示了使用 EJB 和 JPA 实施用户存储提供程序。 | |
| 演示了使用包含用户名/密码密钥对的简单属性文件实施用户存储提供程序。 |
7.1. 供应商接口 复制链接链接已复制到粘贴板!
构建用户存储 SPI 实施时,您必须定义供应商类和供应商工厂。供应商类实例由供应商工厂为每个事务创建。供应商类执行用户查找和其他用户操作的所有繁重。它们必须实施 org.keycloak.storage.UserStorageProvider 接口。
您可能会认为 UserStorageProvider 接口是用户稀疏的稀疏?稍后您将在本章中看到,您的供应商类可能实施的其他混合接口来支持用户集成的机制。
UserStorageProvider 实例为每个事务创建一次。事务完成后,会调用 UserStorageProvider.close () 方法,然后实例会被垃圾回收。实例由供应商工厂创建。供应商工厂实现了 org.keycloak.storage.UserStorageProviderFactory 接口。
在实施 UserStorageProviderFactory 时,供应商工厂类必须将 concrete 供应商类指定为模板参数。这是一个必须,因为运行时将内省此类以扫描其功能(其实施的其他接口)。例如,如果您的供应商类命名为 FileProvider,则工厂类应类似如下:
getId () 方法返回用户存储提供程序的名称。当您想为特定域启用供应商时,控制台的 User Federation 页面中会显示此 id。
create () 方法负责分配提供程序类实例。它使用一个 org.keycloak.models.KeycloakSession 参数。此对象可用于查找其他信息和元数据,以及提供对运行时各种其他组件的访问。ComponentModel 参数代表如何在特定域中启用和配置该提供程序。它包含已启用供应商的实例 ID,以及通过管理控制台启用时可能会为其指定的任何配置。
UserStorageProviderFactory 具有其他功能,我们将在本章稍后进行。
7.2. 供应商功能接口 复制链接链接已复制到粘贴板!
如果您仔细检查了 UserStorageProvider 接口,您可能会注意到它没有定义查找或管理用户的任何方法。这些方法实际上在其他功能 接口 中定义,具体取决于外部用户存储可以提供和执行的功能范围。例如,一些外部存储是只读的,只能执行简单的查询和凭据验证。您只需要为您可以正常工作的功能实施 功能接口。您可以实现这些接口:
| SPI | 描述 |
|---|---|
|
| 如果要与来自此外部存储的用户登录,则需要此接口。大多数(全部)提供商实施此接口。 |
|
| 定义用于查找一个或多个用户的复杂查询。如果要从管理控制台查看和管理用户,您必须实施此接口。 |
|
| 如果您的供应商支持计数查询,请实施此接口。 |
|
|
这个接口是 |
|
| 如果您的供应商支持添加和删除用户,请实施此接口。 |
|
| 如果您的供应商支持批量更新一组用户,请实施此接口。 |
|
| 如果您的供应商可以验证一个或多个不同的凭证类型(例如,如果您的供应商可以验证密码),请实施此接口。 |
|
| 如果您的供应商支持更新一个或多个不同的凭证类型,请实施此接口。 |
7.3. 模型接口 复制链接链接已复制到粘贴板!
功能 接口 中定义的大多数方法返回或以用户的表示形式传递。这些表示由 org.keycloak.models.UserModel 接口定义。实现此接口需要应用程序开发人员。它提供了外部用户存储和红帽构建的 Keycloak 使用的用户 metamodel 之间的映射。
UserModel 实现提供对用户读取和更新元数据的访问权限,包括用户名、名称、电子邮件、角色和组映射等内容,以及其他任意属性。
org.keycloak.models 软件包中还有其他模型类,它们代表红帽构建的 Keycloak metamodel: RealmModel、RoleModel、GroupModel 和 ClientModel 的其他部分。
7.3.1. 存储 Ids 复制链接链接已复制到粘贴板!
UserModel 的一个重要方法是 getId () 方法。在实施 UserModel 开发人员时,必须了解用户 id 格式。格式必须是:
"f:" + component id + ":" + external id
"f:" + component id + ":" + external id
红帽构建的 Keycloak 运行时通常必须通过用户 ID 查找用户。用户 id 包含足够信息,以便运行时不必查询系统中的每个 UserStorageProvider 来查找用户。
组件 id 是从 ComponentModel.getId () 返回的 id。在创建供应商类时,component Model 作为参数传递,以便您可以从那里获取它。外部 ID 是您的供应商类需要在外部存储中查找用户的信息。这通常是用户名或 uid。例如,它可能类似如下:
f:332a234e31234:wburke
f:332a234e31234:wburke
当运行时按 id 进行查找时,会解析 id 来获取组件 ID。组件 ID 用于找到最初用于加载用户的 UserStorageProvider。然后,该提供程序通过 id。该提供程序再次解析 id 以获取外部 ID,它将用于在外部用户存储中查找用户。
7.4. 打包和部署 复制链接链接已复制到粘贴板!
为了让红帽构建的 Keycloak 识别提供程序,您需要向 JAR 添加一个文件: META-INF/services/org.keycloak.storage.UserStorageProviderFactory。此文件必须包含 UserStorageProviderFactory 实现的完全限定类名称列表:
org.keycloak.examples.federation.properties.ClasspathPropertiesStorageFactory org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
org.keycloak.examples.federation.properties.ClasspathPropertiesStorageFactory
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
要部署此 jar,将其复制到 provider/ 目录中,然后运行 bin/kc.[sh|bat] build。
7.5. 简单的只读,查找示例 复制链接链接已复制到粘贴板!
为了说明实施用户存储 SPI 的基础知识,让执行一个简单的示例。在本章中,您将看到一个简单的 UserStorageProvider 实施,用于在简单的属性文件中查找用户。属性文件包含用户名和密码定义,并且硬编码到 classpath 上的特定位置。提供程序将根据 ID 和用户名查找用户,并能够验证密码。源自此提供程序的用户将是只读的。
7.5.1. 供应商类 复制链接链接已复制到粘贴板!
我们首先进行的操作是 UserStorageProvider 类。
我们的供应商类 PropertyFileUserStorageProvider 实现了许多接口。它实现了 UserStorageProvider,因为它是 SPI 的基本要求。它实现了 UserLookupProvider 接口,因为我们需要使用此提供程序存储的用户登录。它实现了 CredentialInputValidator 接口,因为我们希望使用登录屏幕验证输入的密码。我们的属性文件是只读的。我们实现了 CredentialInputUpdater,因为当用户尝试更新其密码时,我们希望发布错误条件。
此提供程序类的结构将存储对 KeycloakSession、componentModel 和属性文件的引用。我们稍后将使用所有这些内容。另请注意,已加载的用户映射。每当我们发现用户在此映射中时,我们都会避免在相同的事务中再次重新创建它。这是遵循许多供应商需要执行此操作(即,与 JPA 集成的任何供应商)的好做法。请记住,每个事务都会创建供应商类实例,并在事务完成后关闭。
7.5.1.1. UserLookupProvider 实现 复制链接链接已复制到粘贴板!
当用户登录时,Red Hat build of Keycloak 登录页会调用 getUserByUsername () 方法。在我们的实施中,我们首先检查 loadedUsers 映射,以查看该用户是否已加载到此事务中。如果还没有加载,我们查看用户的属性文件。如果存在,我们创建一个 UserModel 的实现,将其存储在 加载的Users 中,以备将来参考,并返回此实例。
createAdapter () 方法使用帮助程序类 org.keycloak.storage.adapter.AbstractUserAdapter。这为 UserModel 提供基本实施。它使用用户的用户名作为外部 id,它根据所需的存储 id 格式自动生成用户 ID。
"f:" + component id + ":" + username
"f:" + component id + ":" + username
AbstractUserAdapter 的每个 get 方法返回 null 或空集合。但是,返回角色和组映射的方法将返回为每个用户配置的域的默认角色和组。AbstractUserAdapter 的每个集合都会抛出 org.keycloak.storage.ReadOnlyException。因此,如果您试图修改管理控制台中的用户,则会出现错误。
getUserById () 方法使用 org.keycloak.storage.StorageId helper 类解析 id 参数。调用 StorageId.getExternalId () 方法,以获取嵌入在 id 参数中的用户名。然后,该方法将委派给 getUserByUsername ()。
电子邮件不存储,因此 getUserByEmail () 方法返回 null。
7.5.1.2. CredentialInputValidator 实现 复制链接链接已复制到粘贴板!
接下来,查看 CredentialInputValidator 的方法实现。
运行时调用 isConfiguredFor () 方法,以确定是否为用户配置了特定凭证类型。此方法检查是否为用户设置密码。
supportsCredentialType () 方法返回是否针对特定凭证类型支持验证。我们检查凭据类型是 密码。
isValid () 方法负责验证密码。CredentialInput 参数实际上只是适用于所有凭证类型的抽象接口。我们确保支持凭证类型,也是 UserCredentialModel 实例。当用户通过登录页面登录时,密码输入的纯文本将放入 UserCredentialModel 实例中。isValid () 方法根据存储在属性文件中的纯文本密码检查此值。返回值为 true 表示密码有效。
7.5.1.3. CredentialInputUpdater 实现 复制链接链接已复制到粘贴板!
如前所述,我们实现这个示例中的 CredentialInputUpdater 接口的唯一原因是禁止修改用户密码。我们必须这样做的原因是,否则运行时允许在红帽构建的 Keycloak 本地存储中覆盖密码。本章稍后将讨论更多相关信息。
updateCredential () 方法只检查凭证类型是否为 password。如果是,则会抛出 ReadOnlyException。
7.5.2. 供应商工厂实施 复制链接链接已复制到粘贴板!
现在,供应商类已完成,我们现在将注意到供应商工厂类。
首先要注意的是,在实施 UserStorageProviderFactory 类时,您必须传递 concrete 供应商类实施作为模板参数。在这里,我们指定之前定义的供应商类: PropertyFileUserStorageProvider。
如果没有指定 template 参数,您的供应商将无法正常工作。运行时执行类内省来确定提供程序实施 的功能接口。
getId () 方法标识运行时中的工厂,当您要为域启用用户存储供应商时,也将是管理控制台中显示的字符串。
7.5.2.1. 初始化 复制链接链接已复制到粘贴板!
UserStorageProviderFactory 接口有一个可选的 init () 方法,您可以实现。当红帽构建的 Keycloak 引导时,只会为每个供应商工厂创建一个实例。在引导时,每个工厂实例都会调用 init () 方法。还有一个 postInit () 方法,也可以实现。调用每个工厂的 init () 方法后,会调用其 postInit () 方法。
在 init () 方法实施中,我们从 classpath 找到包含用户声明的属性文件。然后,我们使用存储在其中的用户名和密码的组合来加载 properties 字段。
Config.Scope 参数是通过服务器配置配置的工厂配置。
例如,使用以下参数运行服务器:
kc.[sh|bat] start --spi-storage-readonly-property-file-path=/other-users.properties
kc.[sh|bat] start --spi-storage-readonly-property-file-path=/other-users.properties
我们可以指定用户属性文件的类路径,而不是硬编码它。然后您可以在 PropertyFileUserStorageProviderFactory.init () 中检索配置:
7.5.2.2. 创建方法 复制链接链接已复制到粘贴板!
创建提供程序工厂的最后一步是 create () 方法。
@Override
public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) {
return new PropertyFileUserStorageProvider(session, model, properties);
}
@Override
public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) {
return new PropertyFileUserStorageProvider(session, model, properties);
}
我们只是分配 PropertyFileUserStorageProvider 类。这个创建方法将为每个事务调用一次。
7.5.3. 打包和部署 复制链接链接已复制到粘贴板!
我们提供程序实施的类文件应放在 jar 中。您还必须在 META-INF/services/org.keycloak.storage.UserStorageProviderFactory 文件中声明供应商工厂类。
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
org.keycloak.examples.federation.properties.FilePropertiesStorageFactory
要部署此 jar,将其复制到 provider/ 目录中,然后运行 bin/kc.[sh|bat] build。
7.5.4. 在管理门户中启用供应商 复制链接链接已复制到粘贴板!
您可以在管理控制台的 User Federation 页面中启用每个域的用户存储供应商。
用户联邦
流程
从列表中选择我们刚才创建的提供程序:
readonly-property-file。此时会显示我们的提供程序的配置页面。
点 Save,因为没有配置。
配置的供应商
返回到主 User Federation 页面
现在,您会看到列出了您的供应商。
用户联邦
现在,您将能够使用 users.properties 文件中声明的用户登录。此用户只能在登录后查看帐户页面。
7.6. 配置技术 复制链接链接已复制到粘贴板!
PropertyFileUserStorageProvider 示例是一个位聚合。它被硬编码到嵌入在提供程序的 jar 中的属性文件,这并不很有用。我们可能希望使此文件的位置可以为每个提供程序实例进行配置。换句话说,我们可能在多个不同的域中多次重复使用此提供程序,并指向完全不同的用户属性文件。我们还希望在管理控制台 UI 中执行此配置。
UserStorageProviderFactory 还有额外的方法,您可以实现用于处理供应商配置。您描述了您要为每个提供程序配置的变量,而管理控制台会自动呈现通用输入页面来收集此配置。在实施时,回调方法也会在保存配置前验证配置,当第一次创建提供程序以及更新时。UserStorageProviderFactory 从 org.keycloak.component.ComponentFactory 接口继承这些方法。
ComponentFactory.getConfigProperties () 方法返回 org.keycloak.provider.ProviderConfigProperty 实例的列表。这些实例声明了呈现和存储提供程序的每个配置变量所需的元数据。
7.6.1. 配置示例 复制链接链接已复制到粘贴板!
我们来展开我们的 PropertyFileUserStorageProviderFactory 示例,允许您将供应商实例指向磁盘上的特定文件。
PropertyFileUserStorageProviderFactory
ProviderConfigurationBuilder 类是一个很好的帮助程序类,用于创建配置属性列表。在这里,我们指定了一个名为 path 的变量,它是一个字符串类型。在此提供程序的 Admin Console 配置页面中,此配置变量被标记为 Path,默认值为 ${jboss.server.config.dir}/example-users.properties。当您将鼠标悬停在此配置选项的工具提示时,它会显示帮助文本,指向属性文件的文件名。
下面我们将做的下一个操作是验证磁盘上是否存在此文件。除非它指向有效的用户属性文件,我们不想在域中启用此提供程序的实例。为此,我们实施 validateConfiguration () 方法。
validateConfiguration () 方法提供 组件Model 中的配置变量,以验证磁盘上是否存在该文件。请注意,使用 org.keycloak.common.util.EnvUtil.replace () 方法。使用这个方法,包含 ${} 的任何字符串都将用系统属性值替换该值。${jboss.server.config.dir} 字符串对应于服务器的 conf/ 目录,这对于本例来说非常有用。
接下来,我们必需做的是删除旧的 init () 方法。我们这样做,因为用户属性文件将为每个供应商实例是唯一的。我们将此逻辑移到 create () 方法。
这个逻辑是,由于每个事务都从磁盘读取整个用户属性文件,但希望以简单方式,在配置变量中如何 hook,所以其逻辑都效率较低。
7.6.2. 在管理门户中配置提供程序 复制链接链接已复制到粘贴板!
现在,启用了配置,您可以在管理控制台中配置供应商时设置 path 变量。
7.7. 添加/删除用户和查询功能接口 复制链接链接已复制到粘贴板!
我们没有通过我们的示例完成的一个操作是允许它添加和删除用户或更改密码。在我们的示例中定义的用户也可以在管理控制台中查询或查看。要添加这些增强功能,我们的示例供应商必须实施 UserQueryMethodsProvider (或 UserQueryProvider)和 UserRegistrationProvider 接口。
7.7.1. 实现 UserRegistrationProvider 复制链接链接已复制到粘贴板!
使用这个流程实现从特定存储中添加和删除用户,我们首先需要将属性文件保存到磁盘。
PropertyFileUserStorageProvider
然后,addUser () 和 removeUser () 方法的实现变得简单。
PropertyFileUserStorageProvider
请注意,在添加用户时,我们将属性映射的密码值设置为 UNSET_PASSWORD。我们这样做,因为我们没有属性值中属性的 null 值。我们还必须修改 CredentialInputValidator 方法以反映这一点。
如果提供程序实现了 UserRegistrationProvider 接口,则将调用 addUser () 方法。如果您的供应商有一个配置开关来为用户关闭,请从此方法返回 null 将跳过供应商并调用下一个供应商。
PropertyFileUserStorageProvider
由于我们现在可以保存我们的属性文件,因此允许密码更新也有意义。
PropertyFileUserStorageProvider
现在,我们还可以实施禁用密码。
PropertyFileUserStorageProvider
实施这些方法后,您现在可以更改并禁用管理控制台中用户的密码。
7.7.2. 实现 UserQueryProvider 复制链接链接已复制到粘贴板!
UserQueryProvider 是 UserQueryMethodsProvider 和 UserCountMethodsProvider 的组合。如果没有实现 UserQueryMethodsProvider,则管理控制台将无法查看和管理我们示例供应商加载的用户。我们来了解如何实施此接口。
PropertyFileUserStorageProvider
searchForUserStream () 的第一个声明采用 String 参数。在本例中,参数代表您要搜索的用户名。此字符串可以是子字符串,它解释了执行搜索时 String.contains () 方法的选择。请注意,使用 * 表示请求所有用户的列表。该方法迭代属性文件的关键集合,委派为 getUserByUsername () 来加载用户。请注意,我们根据 firstResult 和 maxResults 参数索引此调用。如果您的外部存储不支持分页,则必须执行类似的逻辑。
PropertyFileUserStorageProvider
使用 Map 参数的 searchForUserStream () 方法可以根据名字、姓氏、用户名和电子邮件搜索用户。仅存储用户名,因此搜索仅基于用户名,除了 Map 参数不包含 username 属性时除外。在这种情况下,所有用户都会被返回。在这种情况下,使用 searchForUserStream (realm, search, firstResult, maxResults)。
PropertyFileUserStorageProvider
组或属性不会被存储,因此其他方法会返回空流。
7.8. 增加外部存储 复制链接链接已复制到粘贴板!
PropertyFileUserStorageProvider 示例实际有限。虽然我们可以与存储在属性文件中的用户登录,但我们将无法做更多操作。如果此提供程序加载的用户需要特殊的角色或组映射来完全访问特定应用程序,则无法向这些用户添加额外的角色映射。您还可以修改或添加额外的重要属性,如 email、first 和 last name。
对于这些类型的情况,红帽构建的 Keycloak 允许您通过在红帽构建的 Keycloak 数据库中存储额外信息来增加外部存储。这称为联邦用户存储,它封装在 org.keycloak.storage.federated.UserFederatedStorageProvider 类中。
UserFederatedStorageProvider
UserFederatedStorageProvider 实例在 UserStorageUtil.userFederatedStorage (KeycloakSession) 方法上提供。它具有各种不同类型的方法来存储属性、组和角色映射、不同的凭证类型和所需操作。如果您的外部存储的 datamodel 无法支持完整的红帽构建的 Keycloak 功能集,则该服务可能会填补差距。
红帽构建的 Keycloak 附带了帮助程序类 org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage,它将把每个单个 UserModel 方法委托给用户联邦存储。覆盖需要覆盖的方法,以委派给外部存储表示。强烈建议您阅读此类的 javadoc,因为它有较小的保护方法,您可能需要覆盖。指定组成员资格和角色映射。
7.8.1. 多租户示例 复制链接链接已复制到粘贴板!
在我们的 PropertyFileUserStorageProvider 示例中,我们只需要对我们的供应商进行简单的更改,以使用 AbstractUserAdapterFederatedStorage。
PropertyFileUserStorageProvider
相反,我们定义了 AbstractUserAdapterFederatedStorage 的匿名类实现。setUsername () 方法对属性文件进行更改并保存。
7.9. 导入实施策略 复制链接链接已复制到粘贴板!
在实施用户存储供应商时,您可以采用另一个策略。除了使用用户联邦存储外,您可以在红帽构建的 Keycloak 内置用户数据库中在本地创建用户,并将外部存储的属性复制到这个本地副本。这种方法有很多优点。
- 红帽构建的 Keycloak 基本上成为外部存储的持久性用户缓存。用户导入后,您将不再按外部存储,从而关闭它。
- 如果您要以您的官方用户存储的形式迁移到红帽 Keycloak,并弃用旧的外部存储,您可以缓慢迁移应用程序以使用红帽构建的 Keycloak。迁移所有应用程序时,取消链接导入的用户,并停用旧的外部存储。
使用导入策略有一些明显的缺点:
- 第一次查找用户将需要对红帽构建的 Keycloak 数据库进行多次更新。这可能会因为负载造成大量性能损失,并在红帽构建的 Keycloak 数据库上造成大量压力。用户联邦存储方法将仅根据需要存储额外的数据,且可能不得根据外部存储的功能使用。
- 通过导入方法,您必须保持本地红帽构建的 Keycloak 存储和外部存储同步。用户存储 SPI 具有可用于支持同步的功能接口,但这可能会快速实现。
要实施导入策略,只需检查以首先查看用户是否在本地导入。如果是这样,如果没有在本地创建用户并从外部存储导入数据,则返回本地用户。您还可以代理本地用户,以便大多数更改会自动同步。
这将是一个位约束,但我们可以扩展我们的 PropertyFileUserStorageProvider 来实现这种方法。首先,我们首先修改 createAdapter () 方法。
PropertyFileUserStorageProvider
在这个方法中,我们调用 UserStoragePrivateUtil.userLocalStorage (session) 方法来获取对本地红帽构建的 Keycloak 用户存储的引用。如果没有,我们会看到用户是否存储在本地。我们会在本地添加它。不要设置本地用户的 id。让红帽构建的 Keycloak 会自动生成 id。另请注意,我们调用 UserModel.setFederationLink (),并传递我们供应商的 ComponentModel 的 ID。这会在供应商和导入的用户之间建立链接。
删除用户存储提供程序时,也会删除由它导入的任何用户。这是调用 UserModel.setFederationLink () 的目的之一。
需要注意的是,如果链接了本地用户,您的存储供应商仍会委托给它从 CredentialInputValidator 和 CredentialInputUpdater 接口实现的方法。从验证或更新中返回 false 会导致红帽构建的 Keycloak 查看是否使用本地存储验证或更新。
另请注意,我们使用 org.keycloak.models.utils.UserModelDelegate 类代理本地用户。此类是 UserModel 的实现。每个方法都只委派给它实例化的 UserModel。我们覆盖此委派类的 setUsername () 方法,以自动与属性文件同步。对于供应商,您可以使用此选项 截获 本地 UserModel 上的其他方法,以从外部存储执行同步。例如,get 方法可以确保本地存储同步。设置方法使外部存储与本地存储保持同步。需要注意的是 getId () 方法应始终返回在本地创建用户时自动生成的 id。您不应该返回联邦 ID,如其他非导入示例所示。
如果您的供应商正在实施 UserRegistrationProvider 接口,则您的 removeUser () 方法不需要从本地存储中删除该用户。运行时将自动执行此操作。另请注意,在从本地存储中删除之前,将调用 removeUser ()。
7.9.1. ImportedUserValidation 接口 复制链接链接已复制到粘贴板!
如果您之前在本章前面提到,我们将讨论如何查询用户工作。首先会查询本地存储(如果用户在此处找到),则查询将结束。对于上述实现,这是一个问题,因为我们希望代理本地 UserModel,以便我们可以同步用户名。用户存储 SPI 具有回调,每当链接的本地用户从本地数据库加载时。
每当加载链接的本地用户时,如果用户存储供应商类实施此接口,则调用 validate () 方法。您可以在这里代理作为参数传递的本地用户并返回它。将使用该新 UserModel。您还可以选择性地进行检查来查看用户是否仍然存在于外部存储中。如果 validate () 返回 null,则本地用户将从数据库中删除。
7.9.2. ImportSynchronization 接口 复制链接链接已复制到粘贴板!
使用 import 策略,您可以看到本地用户副本可以与外部存储不同步。例如,用户可能已经从外部存储中删除。用户存储 SPI 有一个额外的接口,您可以实现来处理此接口 org.keycloak.storage.user.ImportSynchronization :
此接口由提供商工厂实现。当此界面由提供商工厂实施后,供应商的管理控制台管理页面会显示其他选项。您可以通过单击按钮来手动强制同步。这会调用 ImportSynchronization.sync () 方法。另外,还会显示额外的配置选项,供您自动调度同步。自动同步调用 syncSince () 方法。
7.10. 用户缓存 复制链接链接已复制到粘贴板!
当用户对象由 ID、用户名或电子邮件查询加载时,会缓存它。当用户对象被缓存时,它会迭代整个 UserModel 接口,并将这些信息拉取到本地仅内存缓存中。在集群中,这个缓存仍然是本地的,但它成为无效缓存。修改用户对象时,它将被驱除。此驱除事件会被传播到整个集群,以便其他节点的用户缓存也无效。
7.10.1. 管理用户缓存 复制链接链接已复制到粘贴板!
您可以通过调用 KeycloakSession.getProvider (UserCache.class) 来访问用户缓存。
有方法可以驱除特定用户、特定域中包含的用户或整个缓存。
7.10.2. OnUserCache 回调接口 复制链接链接已复制到粘贴板!
您可能想要缓存特定于您的供应商实现的额外信息。用户存储 SPI 在用户缓存时会有一个回调: org.keycloak.models.cache.OnUserCache。
public interface OnUserCache {
void onCache(RealmModel realm, CachedUserModel user, UserModel delegate);
}
public interface OnUserCache {
void onCache(RealmModel realm, CachedUserModel user, UserModel delegate);
}
如果您希望此回调,您的供应商类应实施此接口。UserModel delegate 参数是您的供应商返回的 UserModel 实例。CachedUserModel 是一个扩展的 UserModel 接口。这是本地缓存在本地存储中的实例。
此 CachedUserModel 接口允许您从缓存中驱除用户,并获取供应商 UserModel 实例。getCachedWith () 方法返回一个映射,供您缓存与用户相关的其他信息。例如,凭证不是 UserModel 接口的一部分。如果要在内存中缓存凭证,您将实施 OnUserCache,并使用 getCachedWith () 方法缓存用户的凭证。
7.10.3. 缓存策略 复制链接链接已复制到粘贴板!
在用户存储供应商的管理控制台管理页面中,您可以指定唯一的缓存策略。
7.11. 利用 Jakarta EE 复制链接链接已复制到粘贴板!
从版本 20 开始,Keycloak 仅适用于 Quarkus。与 WildFly 不同,Quarkus 不是应用服务器。详情请查看 https://www.keycloak.org/migration/migrating-to-quarkus#_quarkus_is_not_an_application_server。
因此,用户存储提供程序无法打包在任何 Jakarta EE 组件中,或者像 Keycloak 在之前的版本中通过 WildFly 运行时一样。
供应商实现必须是实现合适的 User Storage SPI 接口的普通 java 对象,如上一节中所述。如本迁移指南中所述,必须打包和部署它们:
您仍然可以实现自定义 UserStorageProvider 类,该类可通过 JPA Entity Manager 集成外部数据库,如下例所示:
不支持 CDI。
7.12. REST 管理 API 复制链接链接已复制到粘贴板!
您可以通过管理员 REST API 创建、删除和更新用户存储供应商部署。用户存储 SPI 基于通用组件接口构建,因此您将使用该通用 API 来管理您的供应商。
REST 组件 API 位于您的 realm admin 资源下。
/admin/realms/{realm-name}/components
/admin/realms/{realm-name}/components
我们仅显示与 Java 客户端的 REST API 交互。您希望您可以从这个 API 中提取如何从 curl 执行此操作。
要创建用户存储供应商,您必须指定供应商 ID、字符串 org.keycloak.storage.UserStorageProvider 的供应商类型以及配置。
7.13. 从以前的用户联邦 SPI 迁移 复制链接链接已复制到粘贴板!
只有在使用之前(并已被删除)用户 Federation SPI 时,本章才适用。
在 Keycloak 版本 2.4.0 及更早版本中有一个 User Federation SPI。Red Hat Single Sign-On 版本 7.0 (但不受支持)也提供了早期的 SPI 版本。此早期的 User Federation SPI 已从 Keycloak 版本 2.5.0 和 Red Hat Single Sign-On 版本 7.1 中删除。但是,如果您使用此早期 SPI 编写了供应商,本章讨论了可用于端口的一些策略。
7.13.1. 导入而不是非导入 复制链接链接已复制到粘贴板!
之前的用户 Federation SPI 要求您在红帽构建的 Keycloak 数据库中创建用户的本地副本,并将信息从外部存储导入到本地副本。但是,这不再是必需的。您仍然可以按原样方式移植之前的供应商,但您应该考虑非重要策略是否是更好的方法。
导入策略的优点:
- 红帽构建的 Keycloak 基本上成为外部存储的持久性用户缓存。用户导入后,您将不再按外部存储,从而关闭它。
- 如果您要以您的官方用户存储和弃用早期的外部存储迁移到 Red Hat build of Keycloak,您可以缓慢迁移应用程序以使用红帽构建的 Keycloak。迁移所有应用程序时,取消链接导入的用户,并停用较早的传统外部存储。
使用导入策略有一些明显的缺点:
- 第一次查找用户将需要对红帽构建的 Keycloak 数据库进行多次更新。这可能会因为负载造成大量性能损失,并在红帽构建的 Keycloak 数据库上造成大量压力。用户联邦存储方法将仅根据需要存储额外的数据,且可能不得根据外部存储的功能使用。
- 通过导入方法,您必须保持本地红帽构建的 Keycloak 存储和外部存储同步。用户存储 SPI 具有可用于支持同步的功能接口,但这可能会快速实现。
7.13.2. UserFederationProvider 和 UserStorageProvider 复制链接链接已复制到粘贴板!
首先要注意到的是 UserFederationProvider 是一个完整的接口。在此界面中实施了每种方法。但是,UserStorageProvider 已将这个接口分成您根据需要实现的多个功能接口。
UserFederationProvider.getUserByUsername () 和 getUserByEmail () 在新的 SPI 中完全匹配。两者之间的区别在于如何导入。如果您要继续导入策略,您将不再调用 KeycloakSession.userStorage ().addUser () 来在本地创建用户。相反,您可以调用 KeycloakSession.userLocalStorage ().addUser ()。userStorage () 方法不再存在。
UserFederationProvider.validateAndProxy () 方法已移到可选的功能接口 ImportedUserValidation 中。如果您要按预期移植以前的供应商,则需要实施此接口。另请注意,在之前的 SPI 中,这个方法在每次用户访问时都会被调用,即使本地用户位于缓存中。在后续的 SPI 中,只有本地用户从本地存储加载时,才会调用此方法。如果本地用户被缓存,则不会调用 ImportedUserValidation.validate () 方法。
UserFederationProvider.isValid () 方法在以后的 SPI 中不再存在。
UserFederationProvider 方法 synchronizeRegistrations ()、registerUser () 和 removeUser () 已移至 UserRegistrationProvider 功能接口。这个新接口是可选的,因此如果您的供应商不支持创建和删除用户,则不需要实施它。如果您之前的供应商有切换对注册新用户的支持,则在新的 SPI 中被支持,如果供应商不支持添加用户,则从 UserRegistrationProvider.addUser () 返回 null。
较早的 UserFederationProvider 方法现在封装在 CredentialInputValidator 和 CredentialInputUpdater 接口中,具体取决于您是否支持验证或更新凭证。用于存在于 UserModel 方法中的凭证管理。它们也已移到 CredentialInputValidator 和 CredentialInputUpdater 接口。请注意,如果您没有实现 CredentialInputUpdater 接口,则您的供应商提供的任何凭证都可以在红帽构建的 Keycloak 存储的本地覆盖。因此,如果您希望凭证为只读,请实施 CredentialInputUpdater.updateCredential () 方法并返回 ReadOnlyException。
UserFederationProvider 查询方法,如 searchByAttributes () 和 getGroupMembers () 现在被封装在可选的 interface UserQueryProvider 中。如果您不实现此接口,则用户将无法在管理控制台中查看。您仍能够登录。
之前的 SPI 中的同步方法现在封装在可选的 ImportSynchronization 接口中。如果您实施了同步逻辑,则有新的 UserStorageProviderFactory 实现 ImportSynchronization 接口。
7.13.4. 升级到新模型 复制链接链接已复制到粘贴板!
用户存储 SPI 实例存储在不同的关系表中。红帽构建的 Keycloak 会自动运行一个迁移脚本。如果为域部署了任何早期的用户联邦供应商,则它们将转换为更新的存储模型,包括数据的 id。只有存在与之前的用户联邦提供程序相同的供应商 ID (如"ldap"、"kerberos")的用户存储供应商时,才会进行此迁移。
因此,您可以采取不同的方法。
- 您可以在以前的红帽构建的 Keycloak 部署中删除早期的供应商。这将删除您导入的所有用户的本地链接副本。然后,当您升级红帽构建的 Keycloak 时,只需为您的域部署和配置新供应商。
-
第二个选项是编写您的新提供程序,确保其具有相同的供应商 ID:
UserStorageProviderFactory.getId ()。确保此提供程序已部署到服务器。引导服务器,并让内置迁移脚本从以前的数据模型转换为更新的数据模型。在这种情况下,您之前导入的所有用户都将正常工作,并且是相同的。
如果您已决定导入策略并重写您的用户存储供应商,建议您在升级红帽构建的 Keycloak 前删除之前的供应商。这将删除您导入的任何用户的链接本地导入副本。
7.14. 基于流的接口 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 中的许多用户存储接口包含可返回大量对象的查询方法,这可能会导致内存消耗和处理时间出现显著影响。当只有对象内部状态的一个子集在查询方法的逻辑中使用时,这尤其如此。
为了给开发人员提供使用这些查询方法中大型数据集的有效替代方案,在用户存储接口中添加了 Streams 子接口。这些 Streams 子接口将 super-interfaces 中的基于原始集合的方法替换为基于流的变体,从而基于集合的方法。基于集合的查询方法的默认实现调用其 Stream 的对应部分,并收集到正确的集合类型。
Streams 子接口允许实现基于流的方法来处理一组数据,并从该方法的潜在内存和性能优化中受益。提供要实施的 Streams 子接口的接口包括几个 功能接口、org.keycloak.storage.fe deated 软件包中的所有接口,以及可能根据自定义存储实施范围而实现的其它接口。
请参阅此列表,为开发人员提供一个 Streams 子接口的接口列表。
| 软件包 | 类 |
|
|
|
|
|
|
|
| 所有接口 |
|
|
|
7000 表示接口是一个 功能接口
要从流方法中受益的自定义用户存储实施应实施 Streams 子接口而不是原始接口。例如,以下代码使用 UserQueryProvider 接口的 Streams 变体:
第 8 章 Vault SPI 复制链接链接已复制到粘贴板!
8.1. Vault 供应商 复制链接链接已复制到粘贴板!
您可以使用来自 org.keycloak.vault 软件包的 vault SPI 为红帽构建的 Keycloak 编写自定义扩展,以连接到任意 vault 实现。
内置的 file -plaintext 提供程序是实施此 SPI 的示例。通常适用以下规则:
-
要防止 secret 在域中泄漏,您可能需要隔离或限制域可以检索的 secret。在这种情况下,您的供应商应该在查找 secret 时考虑 realm 名称,例如使用 realm 名称添加条目。例如,表达式
${vault.key}将通常评估不同的条目名称,具体取决于它在域 A 还是 realm B 中使用。要区分域,需要从方法传递给创建的 VaultProvider 实例,其中可从VaultProviderFactory.create ()KeycloakSession参数中获得。 -
vault 供应商需要实施单一方法 get
Secret,它为给定机密名称返回VaultRawSecret。该类以byte[]或ByteBuffer为单位包含 secret 的表示,并期望根据需要在两个之间进行转换。请注意,这个缓冲区将在使用量后丢弃,如下所述。
有关如何打包和部署自定义提供程序的详情,请参考 服务提供商接口 章节。
8.2. 使用来自密码库的值 复制链接链接已复制到粘贴板!
库包含敏感数据,红帽构建的 Keycloak 会相应地处理 secret。在访问 secret 时,secret 会从密码库获取,并且仅在必要的时间保留 JVM 内存。然后,所有可能的尝试都会从 JVM 内存丢弃其内容。这可以通过仅在 try-with-resources 语句中使用 vault secret 来实现,如下所示:
这个示例使用 KeycloakSession.vault () 作为访问 secret 的入口点。直接使用 VaultProvider.obtainSecret 方法。但是,vault () 方法具有将原始 secret (通常是字节数组)解释为字符数组(通过 vault ().getCharSecret ())或 String (通过 vault ().getStringSecret ())的好处,以及获取原始未解释的值(通过 vault ().getRawSecret () 方法)。
请注意,由于 String 对象不可变,所以无法通过使用随机垃圾覆盖来丢弃其内容。尽管在默认的 VaultStringSecret 实现中已被采取,但防止内部化 字符串,但存储在 String 对象中的 secret 至少会位于下一个 GC 舍入状态。因此,首选使用普通字节和字符数组和缓冲区。