服务器开发人员指南
第 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 指南中所述。
流程
获取 realm
master
中用户的访问令牌,用户名admin
和密码password
: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
是 属于域 master的机密客户端 -
client_id
启用了Service Accounts Enabled
选项 client_id
有一个自定义"Audience"映射器-
包括的客户端 Audience:
security-admin-console
-
包括的客户端 Audience:
-
-
检查
client_id
是否在"Service Account Roles"选项卡中分配了角色 '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 复制链接链接已复制到粘贴板!
Red Hat build of Keycloak 为网页和电子邮件提供主题支持。这允许自定义面向最终用户的页面的外观和感觉,以便与您的应用程序集成。
图 3.1. 带有 sunrise 示例的登录页面
3.1. 主题类型 复制链接链接已复制到粘贴板!
主题可以提供一个或多个类型来自定义红帽构建的 Keycloak 的不同方面。可用的类型有:
- 帐户 - 帐户控制台
- Admin - 管理控制台
- 电子邮件 - 电子邮件
- login - 登录表单
- Welcome - 欢迎页面
3.2. 配置主题 复制链接链接已复制到粘贴板!
除 welcome 外,所有主题类型都通过管理控制台进行配置。
流程
- 登录管理控制台。
- 从左上角的下拉菜单中选择您的域。
- 从菜单中选择 Realm Settings。
点 Themes 选项卡。
注意要为
master
Admin 控制台设置主题,您需要为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-theme
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
3.3. 默认主题 复制链接链接已复制到粘贴板!
红帽 Keycloak 的构建与服务器分发中的 JAR 文件 keycloak-themes-26.0.15.redhat-00001.jar
中的默认主题捆绑在一起。服务器的根 主题
目录默认不包含任何主题,但它包含一个 README 文件,其中包含有关默认主题的一些额外详情。要简化升级,请不要直接编辑捆绑主题。相反,请创建自己的主题来扩展其中一个捆绑主题。
3.4. 创建主题 复制链接链接已复制到粘贴板!
主题包括:
- HTML 模板(自由标记模板)
- 镜像
- 消息捆绑包
- 风格表
- 脚本
- 主题属性
除非计划替换每个页面,否则您应该扩展另一个主题。最有可能您要扩展某些现有主题。或者,如果您想提供自己的 admin 或帐户控制台实施,请考虑扩展 基础
主题。该 基础
主题由一个消息捆绑包组成,因此此类实施需要从头开始,包括实施主 index.ftl
Freemarker 模板,但它可以使用消息捆绑包中的现有转换。
在扩展主题时,您可以覆盖单个资源(templates、样式表等)。如果您决定覆盖 HTML 模板,您可能需要在升级到新版本时更新自定义模板。
在创建主题时,最好禁用缓存,因为这可以直接从主题目录中编辑主题资源,而无需重启 Red Hat build of 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=false
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 在
themes
目录中创建目录。目录的名称成为主题的名称。例如,要创建一个名为
mytheme
的主题,请创建目录themes/mytheme
。在主题目录中,为您的主题将提供的每个类型创建一个目录。
例如,要将登录类型添加到
mytheme
theme 中,请创建目录themes/mytheme/login
。对于每个类型,创建一个 file
theme.properties
,它允许为主题设置一些配置。例如,要将主题主题/mytheme/login 配置为扩展
基础
主题并导入一些通用资源,请使用以下内容创建文件themes/mytheme/login
/theme.propertiesparent=base import=common/keycloak
parent=base import=common/keycloak
Copy 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 - 以逗号分隔的支持的区域设置列表
有一些属性列表可用于更改用于特定元素类型的 cs 类。如需这些属性的列表,请查看 keycloak theme 对应的类型中的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
目录中创建一个文件。 将此文件添加到
theme.properties
中的styles
属性中。例如,要将
styles.css
添加到mytheme
中,请使用以下内容创建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.css
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 要查看更改,请打开您的域的登录页面。
您会注意到应用的唯一风格是来自您的自定义风格表。
要包含父主题的样式,请从该主题加载样式。编辑
themes/mytheme/login/theme.properties
,并将样式
更改为:styles=css/login.css css/styles.css
styles=css/login.css css/styles.css
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 注意要覆盖父风格表中的样式,请确保最后列出了您的风格表。
3.4.3. 在主题中添加脚本 复制链接链接已复制到粘贴板!
您可以在主题中添加一个或多个脚本。
流程
-
在主题的 <
;THEME TYPE>/resources/js
目录中创建一个文件。 将此文件添加到
theme.properties
中的scripts
属性中。例如,要将
脚本.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.js
Copy 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" alt="My image description">
<img src="${url.resourcesPath}/img/image.jpg" alt="My image description">
3.4.6. 将镜像添加到电子邮件主题 复制链接链接已复制到粘贴板!
要使镜像可用于主题,请将它们添加到主题的 < ;THEME TYPE>/email/resources/img
目录中。它们可以从 直接在 HTML 模板中使用。
例如,将镜像添加到 mytheme
将镜像复制到 themes/mytheme/email/resources/img/logo.jpg
。
要直接在 HTML 模板中使用,请将以下内容添加到自定义 HTML 模板中:
<img src="${url.resourcesUrl}/img/image.jpg" alt="My image description">
<img src="${url.resourcesUrl}/img/image.jpg" alt="My image description">
3.4.7. 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}
和 能)中被替换为参数。例如,
登录 {0} 到 {0}
被替换为域的名称。
这些消息捆绑包的文本可以被特定于域的值覆盖。特定于域的值可以通过 UI 和 API 进行管理。
3.4.8. 在域中添加语言 复制链接链接已复制到粘贴板!
先决条件
- 要为域启用国际化,请参阅 服务器管理指南。
流程
-
在主题的目录中创建文件
<THEME TYPE>/messages/messages_<LOCALE>.properties
。 将此文件添加到 <
THEME TYPE>/theme.properties
中的locales
属性中。要使用户可用的语言是域登录
、帐户和
电子邮件
,主题必须支持语言,因此您需要为主题类型添加您的语言。例如,要将 Norwegian 翻译添加到
mytheme
theme 中,请使用以下内容创建文件themes/mytheme/login/messages/messages_no.properties
:usernameOrEmail=Brukernavn password=Passord
usernameOrEmail=Brukernavn password=Passord
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 如果您省略了消息的翻译,将使用英语。
编辑
themes/mytheme/login/theme.properties
并添加:locales=en,no
locales=en,no
Copy to Clipboard Copied! Toggle word wrap Toggle overflow -
为
帐户
添加相同的内容并通过电子邮件
主题类型。为此,可创建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/messages_en.properties
和themes/mytheme/login/messages/messages_en.properties
:locale_no=Norsk
locale_no=Norsk
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
默认情况下,消息属性文件应使用 UTF-8 进行编码。如果 Keycloak 无法读取内容为 UTF-8,则 Keycloak 会返回 ISO-8859-1 处理。Unicode 字符可以进行转义,如 Java 文档中用于 PropertyResourceBundle 所述。以前的 Keycloak 版本支持在第一行中指定带有注释的编码,如 # encoding: UTF-8
,它不再被支持。
3.4.9. 添加自定义身份提供程序图标 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 支持为自定义身份提供程序添加图标,这些图标显示在登录屏幕上。
流程
-
在登录
theme.properties
文件中定义图标类(例如:themes/mytheme/login/theme.properties
),其键模式为kcLogoIdP-<alias>
。 对于带有别名
myProvider
的身份提供程序,您可以在自定义主题的me.properties
文件中添加一行。例如:kcLogoIdP-myProvider = fa fa-lock
kcLogoIdP-myProvider = fa fa-lock
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
所有图标都位于 PatternFly4 的官方网站上。社交提供程序的图标已在 基本
登录属性(themes/keycloak/login/theme.properties
)中定义,您可以在其中自己激发。
3.4.10. 创建自定义 HTML 模板 复制链接链接已复制到粘贴板!
Red Hat build of Keycloak 使用 Apache Freemarker 模板来生成 HTML 和呈现页面。
虽然可以创建自定义模板来完全改变页面的呈现方式,但建议尽可能利用内置模板。原因包括:
- 在升级过程中,您可能被强制更新自定义模板,以便从更新的版本获取最新的更新
- 通过为主题 配置 CSS 风格,您可以调整 UI 以匹配 UI 设计标准和准则。
- 用户配置文件 允许您支持自定义用户属性并配置它们的呈现方式。
在大多数情况下,您不需要更改模板来根据您的需求调整红帽 Keycloak 的构建,但您可以通过创建 < THEME TYPE>/<TEMPLATE>.ftl 来覆盖您自己的主题中的各个
模板。管理员和帐户控制台使用单个模板 index.ftl
渲染应用程序。
对于其他主题类型中的模板列表,请查看 $KEYCLOAK
; 目录。
_HOME/lib/lib/main/org.keycloak.keycloak-themes-<VERSION>.jar 中的me/base/<THEME_TYPE
>
流程
- 将模板从基础主题复制到您自己的主题。
应用您需要的修改。
例如,要为
mytheme
主题创建一个自定义登录表单,请将themes/base/login/login.ftl
复制到themes/mytheme/login
,并在编辑器中打开它。第一行(<#import …>)后,添加 &
lt;h1>HELLO WORLD!</h1&
gt;,如下所示:<#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 - 备份修改后的模板。当升级到一个新版本的 Red Hat build of Keycloak 时,您可能需要更新自定义模板,以便在适用的情况下对原始模板应用更改。
3.4.11. 电子邮件 复制链接链接已复制到粘贴板!
要编辑电子邮件的主题和内容,如密码恢复电子邮件,请在主题 的电子邮件
类型中添加一个消息捆绑包。每个电子邮件都有三个消息。一个用于主题,一个用于纯文本正文,另一个用于 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
添加到存档中可用的主题的存档中,以及每个主题提供的类型。例如,对于
mytheme
theme 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 的 provider/
目录中,如果已在运行,重启服务器。
第 4 章 基于 React 的 themes 复制链接链接已复制到粘贴板!
管理控制台和帐户控制台基于 React。要完全自定义它们,您可以使用基于 React 的 npm 软件包。有两个软件包:
-
@Keycloak/keycloak-admin-ui
:这是管理控制台的基础主题。 -
@Keycloak/keycloak-account-ui
:这是帐户控制台的基础主题。
npm 上提供了两个软件包。
4.1. 安装软件包 复制链接链接已复制到粘贴板!
要安装软件包,请运行以下命令:
pnpm install @keycloak/keycloak-account-ui
pnpm install @keycloak/keycloak-account-ui
4.2. 使用软件包 复制链接链接已复制到粘贴板!
要使用这些页面,您需要在组件层次结构中添加 KeycloakProvider,以设置要使用的客户端、域和 url。
4.3. 转换页面 复制链接链接已复制到粘贴板!
页面使用 i18next
库进行转换。您可以根据其 [website](https://react.i18next.com/)进行设置。如果要使用提供的翻译,则需要将 i18next-http-backend 添加到项目中并添加:
4.4. 使用页面 复制链接链接已复制到粘贴板!
所有"页面"都是 React 组件,可在您的应用程序中使用。要查看可用的组件,请参阅 [source](https://github.com/keycloak/keycloak/blob/main/js/apps/account-ui/src/index.ts)。或者查看 [quick start](https://github.com/keycloak/keycloak-quickstarts/tree/main/extension/extend-admin-console-node)以了解如何使用它们。
4.5. theme 选择器 复制链接链接已复制到粘贴板!
默认情况下,使用为域配置的主题,但客户端无法覆盖登录主题。此行为可以通过 Theme Selector SPI 来更改。
这可用于通过查看用户代理标头来选择桌面和移动设备的不同主题,例如:
要创建需要实现 ThemeSelectorProviderFactory
和 ThemeSelectorProvider
Provider 的自定义主题选择器。
4.6. 主题资源 复制链接链接已复制到粘贴板!
在 Red Hat build of Keycloak 中实施自定义供应商时,可能需要添加额外的模板、资源和消息捆绑包。
加载额外主题资源的最简单方法是,在me-resources/resources 中的 me-resources/resources 和 messages
。
捆绑包中的
me -resources/templates
资源中创建模板
如果您希望更灵活的方式加载可通过 ThemeResourceSPI 实现的模板和资源。通过实施 ThemeResourceProviderFactory
和 ThemeResourceProvider
,您可以决定如何准确加载模板和资源。
4.7. 区域设置选择器 复制链接链接已复制到粘贴板!
默认情况下,使用 DefaultLocaleSelectorProvider
选择区域设置,它实现了 LocaleSelectorProvider
接口。当禁用国际化时,英语是默认语言。
启用国际化后,会根据 服务器管理指南 中所述的逻辑解析区域设置。
此行为可以通过 LocaleSelector
Provider 和
来更改。
LocaleSelectorProvider
Factory
LocaleSelectorProvider
接口具有单一方法 resolveLocale
,它必须返回给定 RealmModel
和 nullable UserModel
的区域设置。实际请求可以通过 KeycloakSession#getContext
方法获得。
自定义实现可以扩展 DefaultLocaleSelectorProvider
,以便重复使用部分默认行为。例如,要忽略 Accept-Language
请求标头,自定义实施可以扩展默认提供程序,覆盖它的 getAcceptLanguageHeaderLocale
,并返回 null 值。因此,区域设置选择将回退到域的默认语言。
第 5 章 身份代理 API 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 可以将身份验证委托给父 IDP 以进行登录。其中一个典型的示例是您希望用户通过一个社交供应商(如 Facebook 或 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. 客户端启动帐户链接 复制链接链接已复制到粘贴板!
有些应用程序希望与 Facebook 等社交提供商集成,但不想通过这些社交提供商登录。红帽 Keycloak 的构建提供了基于浏览器的 API,应用程序可以使用该 API 将现有用户帐户链接到特定的外部 IDP。这称为客户端发起的帐户链接。帐户链接只能由 OIDC 应用程序启动。
它的工作方式是,应用程序将用户的浏览器转发到红帽构建的 Keycloak 服务器的 URL,要求其要将用户的帐户链接到特定的外部提供程序(如bookbook)。服务器使用外部提供程序启动登录。浏览器从外部提供程序登录,并重定向到服务器。服务器建立链接,并通过确认重定向到应用程序。
在启动此协议前,客户端应用程序必须满足一些条件:
- 必须在管理控制台中为用户域配置并启用所需的身份提供程序。
- 用户帐户必须通过 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 ()
+provider
的 SHA_256 哈希生成。令牌变量从 OIDC 访问令牌获取。您基本上是对随机非ce、用户会话 ID、客户端 ID 和您要访问的身份提供商别名进行哈希处理。
下面是生成 URL 以建立帐户链接的 Java Servlet 代码示例。
为什么包含此哈希?我们这样做的目的是保证身份验证服务器可以保证客户端应用程序发起请求,而其他相关应用程序都没有随机要求用户帐户链接到特定提供程序。身份验证服务器将首先通过检查登录时设置的 SSO cookie 来检查用户是否已登录。然后,它将尝试根据当前登录来重新生成哈希,并将其与应用程序发送的哈希匹配。
链接帐户后,身份验证服务器将重定向到 redirect_uri
。如果提供链接请求存在问题,则 auth 服务器可能会或可能无法重定向到 redirect_uri
。浏览器可能只是在错误页面结束,而不是重新重定向到应用。如果存在错误条件,并且 auth 服务器会足够安全重定向到客户端应用,则额外的 错误
查询参数将附加到 redirect_uri
中。
虽然此 API 保证应用程序启动请求,但它不会完全防止 CSRF 攻击此操作。应用程序仍负责保护 CSRF 攻击目标。
5.2.1. 刷新外部令牌 复制链接链接已复制到粘贴板!
如果您使用通过登录到供应商(例如 Facebook 或 GitHub 令牌)生成的外部令牌,您可以通过重新发起帐户链接 API 来刷新此令牌。
第 6 章 服务供应商接口(SPI) 复制链接链接已复制到粘贴板!
红帽 Keycloak 的构建旨在在不需要自定义代码的情况下涵盖大多数用例,但我们希望它能够自定义。为了实现此红帽构建的 Keycloak 具有多个服务提供商接口(SPI),您可在其中实施自己的供应商。
6.1. 实施 SPI 复制链接链接已复制到粘贴板!
要实施 SPI,您需要实施其 ProviderFactory 和 Provider 接口。您还需要创建服务配置文件。
例如,要实施 Theme Selector SPI,您需要实现 ThemeSelectorProviderFactory 和 ThemeSelectorProvider,并提供文件 META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory
。
ThemeSelectorProviderFactory 示例:
建议您的供应商工厂实施通过方法 getId ()
返回唯一 id。但是,覆盖供应商 部分中所述,此规则可能存在一些例外情况。
红帽 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");
}
如果需要,您的供应商也可以查找其他供应商。例如:
SPI 的 pom.xml 文件需要一个 dependencyManagement
部分,其中包含红帽构建的用于 SPI 的 Keycloak 版本的导入引用。在本例中,将 VERSION
替换为 26.0.15.redhat-00001,这是 Red Hat build of Keycloak 的当前版本。
<.> 将 VERSION
替换为 Red Hat build of Keycloak 的当前版本
6.1.1. 覆盖内置供应商 复制链接链接已复制到粘贴板!
如上所述,建议您的 ProviderFactory
实现使用唯一的 ID。但是,同时覆盖红帽构建的 Keycloak 内置供应商可能很有用。推荐方法是带有唯一 ID 的 ProviderFactory 实现,然后为实例设置默认供应商,如 配置提供程序 章节中所述。另一方面,这可能并不总是可行。
例如,当您需要对默认的 OpenID Connect 协议进行一些自定义,并且您要覆盖 OIDCLoginProtocolFactory
的 Keycloak 实现的默认红帽构建,您需要保留相同的 providerId。例如,admin 控制台、OIDC 协议已知的端点和其他一些因素依赖协议工厂的 ID 为 openid-connect
。
在这种情况下,强烈建议实现自定义实现的方法 顺序()
,并确保它的顺序高于内置实施。
如果有多个具有相同供应商 ID 的实现,红帽构建的 Keycloak 运行时只能使用具有最高顺序的实现。
6.1.2. 显示管理控制台中您的 SPI 实施的信息 复制链接链接已复制到粘贴板!
有时,向红帽构建的 Keycloak 管理员显示有关您的供应商的附加信息。您可以显示供应商构建时间信息(例如,当前安装的自定义供应商版本)、提供程序的当前配置(例如,您的供应商与远程系统通信的远程系统的 url)或一些操作信息(来自您供应商的响应的平均时间)。Red Hat build of Keycloak Admin Console 提供了 Server Info 页面来显示此类信息。
若要显示您的提供程序的信息,足以在您的 ProviderFactory 中实施 org.keycloak.provider.ServerInfoAware
接口。
ProviderFactory
上例中的 MyThemeSelectorProviderFactory
的实现示例:
6.2. 使用可用的供应商 复制链接链接已复制到粘贴板!
在供应商实现中,您可以使用红帽构建的 Keycloak 中提供的其他供应商。现有的提供程序通常可以通过使用 KeycloakSession
来检索,该提供程序可用于您的提供程序,如 实施 SPI 部分中所述。
Red Hat build of 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)
等供应商
多个实现供应商类型 - 称为供应商类型,允许在红帽构建的 Keycloak 运行时提供多个实现。
例如,
EventListener
供应商允许有多个可用的和注册,这意味着特定的事件可以发送到所有监听器(jboss-logging、sysout 等)。在代码中,您可以获取指定的供应商实例,如session.getProvider (EventListener.class, "jboss-logging")
。您需要将provider_id
指定为第二个参数,因为可以有此提供程序类型的多个实例,如上所述。供应商 ID 必须与特定供应商工厂实施的
ProviderFactory.getId ()
返回的 ID 匹配。某些提供程序类型可以通过使用ComponentModel
作为第二个参数来检索,一些(如Authenticator
),甚至需要使用KeycloakSessionFactory
检索。不建议以这种方式实施自己的供应商,因为它可能会在以后被弃用。
6.3. 注册供应商实现 复制链接链接已复制到粘贴板!
通过将 JAR 文件复制到提供程序目录,将 提供程序
注册到服务器。
如果您的供应商需要没有由 Keycloak 提供的额外依赖项,请将它们复制到 供应商
目录中。
注册新供应商或依赖项 Keycloak 后,需要使用非优化启动或 kc.[sh|bat] build
命令重建。
供应商 JAR 不加载在隔离的类加载器中,因此不要包含与内置资源或类冲突的供应商 JAR 中的资源或类。特别是包含 application.properties 文件或覆盖 commons-lang3 依赖项,如果删除了供应商 JAR,则 auto-build 会导致 auto-build 失败。如果您包含冲突的类,您可能会在服务器的启动日志中看到 split package 警告。不幸的是,并非所有内置 lib jar 通过 split 软件包警告逻辑检查,因此您需要在捆绑或包含传输依赖关系之前检查 lib 目录 JAR。如果存在冲突,可以通过删除或重新打包出错类来解决。
如果您已有冲突的资源文件,则不会发出警告。您应该确保 JAR 的资源文件具有包含该提供程序唯一内容的路径名称,或者您可以检查 "install root"/lib/lib/main
目录下的 JAR 内容中是否存在 some.file
,如下所示:
find . -type f -name "*.jar" -exec unzip -l {} \; | grep some.file
find . -type f -name "*.jar" -exec unzip -l {} \; | grep some.file
如果发现您的服务器因为与已删除的供应商 JAR 相关的 NoSuchFileException
错误而无法启动,则运行:
./kc.sh -Dquarkus.launch.rebuild=true --help
./kc.sh -Dquarkus.launch.rebuild=true --help
这将强制 Quarkus 重建相关的索引文件。您可以从那里执行非优化的启动或构建,而无需例外。
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 提供程序 复制链接链接已复制到粘贴板!
脚本 是技术预览,且不受支持。此功能默认为禁用。
使用-- features=preview
or-- features=scripts
启动服务器
Red Hat build of Keycloak 能够在运行时执行脚本,以便管理员能够自定义特定的功能:
- 身份验证器
- JavaScript Policy
- OpenID Connect 协议映射程序
- SAML 协议映射程序
6.4.1. 身份验证器 复制链接链接已复制到粘贴板!
身份验证脚本必须至少提供以下功能之一:auth (..)
,它从 Authenticator"authenticate (AuthenticationFlowContext)
操作调用,它从 action (..)
Authenticator askaction (AuthenticationFlowContext)
调用。
自定义
验证器应至少提供身份验证 (..)
函数。您可以在代码中使用 javax.script.Bindings
脚本。
script
-
用于访问脚本元数据的
ScriptModel
realm
-
RealmModel
user
-
当前的
UserModel
。请注意,当脚本验证器以另一个验证器成功建立用户身份
后触发的身份验证流中配置时,用户就可用,并将用户设置为身份验证会话。 会话
-
活跃的
KeycloakSession
authenticationSession
-
当前
AuthenticationSessionModel
httpRequest
-
the current
org.jboss.resteasy.spi.HttpRequest
LOG
-
org.jboss.logging.Logger
范围为ScriptBasedAuthenticator
您可以从传递给 authentication (context )
参数中提取额外的上下文信息。
action (
函数的 contextcontext
)
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. OpenID Connect 协议映射程序 复制链接链接已复制到粘贴板!
OpenID Connect 协议映射程序脚本是 javascript 脚本,允许您更改 ID Token 和/或 访问令牌的内容。
您可以在代码中使用 javax.script.Bindings
脚本。
user
-
当前
UserModel
realm
-
RealmModel
token
-
当前的
IDToken
。只有在为 ID 令牌配置了映射程序时,它才可用。 tokenResponse
-
当前的
AccessTokenResponse
。只有在为访问令牌配置了映射程序时,它才可用。 userSession
-
active
UserSessionModel
keycloakSession
-
活跃的
KeycloakSession
脚本的导出将用作令牌声明的值。
以上脚本允许从授权请求检索 user_input
。这将可用于映射在映射程序中配置的 Token Claim Name
中。
6.4.3. 使用要部署的脚本创建 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 文件:
此文件应引用您要部署的不同类型的脚本供应商:
authenticators
用于 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.4. 部署脚本 JAR 复制链接链接已复制到粘贴板!
有描述符和您要部署的脚本的 JAR 文件后,您只需要将 JAR 复制到红帽构建的 Keycloak 供应商/目录,然后运行
。
bin/
kc.[sh|bat] 构建
6.5. 可用的 SPI 复制链接链接已复制到粘贴板!
如果要在运行时查看所有可用 SPI 列表,您可以在管理门户中检查 Server Info
页面,如 Admin Console 部分所述。
第 7 章 用户存储 SPI 复制链接链接已复制到粘贴板!
您可以使用用户存储 SPI 为红帽构建的 Keycloak 编写扩展,以连接到外部用户数据库和凭证存储。内置的 LDAP 和 ActiveDirectory 支持是此 SPI 在操作中实现的。开箱即用,红帽构建的 Keycloak 使用其本地数据库来创建、更新和查找用户并验证凭证。通常,机构已有现有的外部专有用户数据库,它们无法迁移到红帽构建的 Keycloak 的数据模型。对于这样的情况,应用程序开发人员可以编写用户存储 SPI 的实现来桥接外部用户存储,以及红帽构建的 Keycloak 用来登录和管理它们的内部用户对象模型。
当红帽构建的 Keycloak 运行时需要查找用户时,比如当用户登录时,它会执行多个步骤来定位用户。首先查看用户是否在用户缓存中;如果用户找到了,则使用该内存中表示。然后,它会在红帽构建的 Keycloak 本地数据库中查找用户。如果没有找到用户,则会循环通过 User Storage SPI 供应商实施来执行用户查询,直到其中一个用户返回运行时查找的用户。供应商查询外部用户存储的用户,并将用户的外部数据表示映射到红帽构建的 Keycloak 用户 metamodel。
用户存储 SPI 供应商实现也可以执行复杂的条件查询,为用户执行 CRUD 操作,验证和管理凭证,或者一次性执行许多用户的批量更新。它取决于外部存储的功能。
用户存储 SPI 提供程序实施的打包并部署与 Jakarta EE 组件类似。默认情况下,它们不会被启用,但必须在管理控制台的 User Federation
选项卡中为每个域启用和配置。
如果您的用户提供程序实施使用一些用户属性作为链接/建立用户身份的元数据属性,请确保用户无法编辑属性,并且相应的属性是只读。示例是 LDAP_ID
属性,它内置的红帽 Keycloak LDAP 供应商构建用于将用户 ID 存储在 LDAP 服务器端。请参阅 Threat 模型缓解章节中 的详细信息。
红帽构建的 Keycloak Quickstarts Repository 中有两个示例项目。每个快速入门都有一个 README
文件,其中包含如何构建、部署和测试示例项目的说明。下表提供了可用用户存储 SPI 快速入门的简单描述:
Name | 描述 |
---|---|
演示使用 JPA 实施用户存储提供程序。 | |
演示使用包含用户名/密码密钥对的简单属性文件实施用户存储提供程序。 |
7.1. 供应商接口 复制链接链接已复制到粘贴板!
在构建用户存储 SPI 的实现时,您必须定义供应商类和供应商工厂。供应商类实例会根据供应商工厂为每个事务创建。供应商类会对用户查找和其他用户操作进行大量工作。它们必须实施 org.keycloak.storage.UserStorageProvider
接口。
您可能会认为 UserStorageProvider
接口是稀疏的?本章稍后会看到,您的供应商可以实施其他混合接口来支持用户集成的机制。
UserStorageProvider
实例会在每次事务创建一次。当事务完成后,会调用 UserStorageProvider.close ()
方法,然后收集实例。实例由供应商工厂创建。Provider factories 实施 org.keycloak.storage.UserStorageProviderFactory
接口。
在实施 UserStorageProviderFactory
时,供应商工厂类必须将 concrete 供应商类指定为 template 参数。这必须作为运行时内省此类,以扫描其功能(它所实施的其他接口)。例如,如果您的供应商类命名为 FileProvider
,则 factory 类应类似如下:
getId ()
方法返回 User Storage 提供程序的名称。当您要为特定域启用供应商时,管理控制台的 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
软件包中的其他模型类代表 Red Hat build of 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。组件Model
在创建供应商类时作为参数传递,以便您可以从那里获取它。外部 id 是您的供应商类需要在外部存储中查找用户的信息。这通常是用户名或 uid。例如,它可能类似如下:
f:332a234e31234:wburke
f:332a234e31234:wburke
当运行时按 id 进行查找时,将解析 id 来获取组件 ID。组件 id 用于找到最初用于加载用户的 UserStorageProvider
。然后,该提供程序通过 id。此提供程序再次解析 id 以获取外部 ID,它将使用 在外部用户存储中定位用户。
这个格式具有可为外部存储用户生成长 ID 的缺陷。这在与 WebAuthn 身份验证 结合使用时,这特别重要,这会将用户处理 ID 限制为 64 字节。因此,如果存储用户使用 WebAuthn 身份验证,则务必要将完整的存储 ID 限制为 64 个字符。method validateConfiguration
可用于在创建时为提供程序组件分配简短 ID,为 64 字节限制的用户 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,请将它复制到 providers/
目录,然后运行 bin/kc.[sh|bat] build
。
7.5. 简单只读、查找示例 复制链接链接已复制到粘贴板!
为了说明实施用户存储 SPI 的基础知识,让我们来逐步介绍一个简单的示例。在本章中,您将看到一个简单的 UserStorageProvider
的实现,该方法可在简单的属性文件中查找用户。属性文件包含用户名和密码定义,并硬编码到 classpath 上的特定位置。提供程序将能够按照 ID 和用户名查找用户,并可验证密码。源自此提供程序的用户将只读。
7.5.1. Provider 类 复制链接链接已复制到粘贴板!
首先,我们将介绍 UserStorageProvider
类。
我们的供应商类 PropertyFileUserStorageProvider
实现了许多接口。它实现了 UserStorageProvider
,因为这是 SPI 的基本要求。它实现了 UserLookupProvider
接口,因为我们希望能够使用此提供商存储的用户登录。它实现了 CredentialInputValidator
接口,因为我们希望可以使用登录屏幕验证输入的密码。我们的属性文件是只读的。我们实施 CredentialInputUpdater
,因为我们希望用户在用户尝试更新其密码时发布错误条件。
此提供程序类的构造器将存储对 KeycloakSession
、ComponentModel
和 property 文件的引用。我们稍后将使用所有这些内容。另请注意,有加载的用户映射。每当我们发现一个用户时,我们将将其存储在此映射中,以便我们避免在同一事务中再次重新创建它。这是遵循许多提供商需要执行此操作的良好做法(即,任何与 JPA 集成的任何供应商)。另请记住,每个事务后都会创建提供程序类实例,并在事务完成后关闭。
7.5.1.1. UserLookupProvider 实现 复制链接链接已复制到粘贴板!
当用户登录时,Red Hat build of Keycloak 登录页面会调用 getUserByUsername ()
方法。在我们的实现中,我们首先检查 loadUsers
映射,以查看该用户是否已加载在这个事务中。如果没有加载,我们会在属性文件中查看 username。如果存在,我们创建一个 UserModel
的实现,将其保存在 loadUsers
中以备将来参考,并返回这个实例。
createAdapter ()
方法使用帮助程序类 org.keycloak.storage.adapter.AbstractUserAdapter
。这为 UserModel
提供了一个基础实施。它使用用户的用户名作为外部 ID,根据所需的存储 id 格式自动生成用户 ID。
"f:" + component id + ":" + username
"f:" + component id + ":" + username
每一个 get 方法的 AbstractUserAdapter
都会返回 null 或空集合。但是,返回角色和组映射的方法将返回为每个用户配置的默认角色和组。AbstractUserAdapter
的每个集合方法都会抛出 org.keycloak.storage.ReadOnlyException
。因此,如果您试图在 Admin 控制台中修改用户,则会出现错误。
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 ()
方法标识运行时中的工厂,当您要为域启用用户存储供应商时,也会是 admin 控制台中显示的字符串。
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
我们可以指定用户属性文件的 classpath,而不是硬编码它。然后,您可以在 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,请将它复制到 providers/
目录,然后运行 bin/kc.[sh|bat] build
。
7.5.4. 在管理门户中启用提供程序 复制链接链接已复制到粘贴板!
您可以在 Admin Console 的 User Federation 页面中为每个域启用用户存储供应商。
用户联邦
流程
从列表中选择刚才创建的提供程序:
readonly-property-file
。此时会显示我们的提供程序的配置页面。
单击 Save,因为我们没有配置任何内容。
配置供应商
返回到主 User Federation 页面
现在,您会看到您的供应商被列出。
用户联邦
现在,您将能够使用 users.properties
文件中声明的用户登录。此用户只能在登录后查看帐户页面。
7.6. 配置技术 复制链接链接已复制到粘贴板!
我们的 PropertyFileUserStorageProvider
示例是一个位拥塞。它被硬编码到提供程序的 jar 中嵌入的属性文件,这并不有用。我们可能希望使此文件的位置可由提供程序的每个实例配置。换句话说,我们可能希望在多个不同的域中多次重复使用此提供程序,并指向完全不同的用户属性文件。我们还将在管理控制台 UI 中执行此配置。
UserStorageProviderFactory
有额外的方法,您可以实现处理提供程序配置的方法。您可以描述您要为每个提供程序配置的变量,Admin Console 会自动呈现一个通用输入页面来收集此配置。实施后,回调方法也会在保存前、首次创建供应商以及更新时验证配置。UserStorageProviderFactory
从 org.keycloak.component.ComponentFactory
接口继承这些方法。
ComponentFactory.getConfigProperties ()
方法返回 org.keycloak.provider.ProviderConfigProperty
实例列表。这些实例声明呈现和存储提供程序的每个配置变量所需的元数据。
7.6.1. 配置示例 复制链接链接已复制到粘贴板!
我们来扩展 PropertyFileUserStorageProviderFactory
示例,允许您将供应商实例指向磁盘上的特定文件。
PropertyFileUserStorageProviderFactory
ProviderConfigurationBuilder
类是一个很好的帮助程序类,用于创建配置属性列表。在这里,我们指定一个名为 path
的变量,它是 String 类型。在此提供程序的 Admin Console 配置页面上,此配置变量标记为 Path
,默认值为 ${jboss.server.config.dir}/example-users.properties
。当您将鼠标悬停在此配置选项的工具提示上时,它会显示帮助文本,文件到属性文件
。
接下来,我们需要做的是验证磁盘上是否存在此文件。我们不想在域中启用此提供程序的实例,除非它指向有效的用户属性文件。为此,我们实施 validateConfiguration ()
方法。
validateConfiguration ()
方法提供 ComponentModel
中的配置变量,以验证磁盘上是否存在该文件。请注意,使用 org.keycloak.common.util.EnvUtil.replace ()
方法。通过此方法,包含 ${}
的任何字符串会将该值替换为系统属性值。${jboss.server.config.dir}
字符串对应于服务器的 conf/
目录,对于本例中非常有用。
接下来,我们需要做的是删除旧的 init ()
方法。我们这样做,因为用户属性文件将为每个提供程序实例是唯一的。我们将此逻辑移到 create ()
方法。
当然,这种逻辑是效率低下,因为每个事务从磁盘读取整个用户属性文件,但希望以简单的方式演示,如何对配置变量中的 hook 进行 hook。
7.6.2. 在管理门户中配置提供程序 复制链接链接已复制到粘贴板!
现在,在启用了配置时,您可以在 Admin 控制台中配置供应商时设置 path
变量。
7.7. 添加/删除用户和查询功能接口 复制链接链接已复制到粘贴板!
我们使用我们的示例的一个操作是允许它添加和删除用户或更改密码。在我们示例中定义的用户也可在管理门户中查询或查看。要添加这些改进,我们的示例供应商必须实现 UserQueryMethodsProvider
(或 UserQueryProvider
)和 UserRegistrationProvider
接口。
7.7.1. 实现 UserRegistrationProvider 复制链接链接已复制到粘贴板!
使用此流程实施从特定存储中添加和删除用户,我们必须首先可以将属性文件保存到磁盘中。
PropertyFileUserStorageProvider
然后,addUser ()
和 removeUser ()
方法的实现变得很简单。
PropertyFileUserStorageProvider
请注意,在添加用户时,我们将属性映射的 password 值设置为 UNSET_PASSWORD
。我们这样做是无法对属性值中的属性有 null 值。我们还必须修改 CredentialInputValidator
方法来反映这一点。
如果供应商实现 UserRegistrationProvider
接口,则会调用 addUser ()
方法。如果您的提供程序有一个配置开关来关闭添加用户,则从此方法返回 null
将跳过提供程序并调用下一个用户。
PropertyFileUserStorageProvider
由于我们现在可以保存属性文件,因此允许密码更新也很有意义。
PropertyFileUserStorageProvider
现在,我们可以实施禁用密码。
PropertyFileUserStorageProvider
通过实现这些方法,您现在可以在管理控制台中更改和禁用用户的密码。
7.7.2. 实现 UserQueryProvider 复制链接链接已复制到粘贴板!
UserQueryProvider
是 UserQueryMethodsProvider
和 UserCountMethodsProvider
的组合。如果没有实施 UserQueryMethodsProvider
,Admin 控制台将无法查看和管理由我们的示例供应商载入的用户。让我们来看看实施此接口。
PropertyFileUserStorageProvider
searchForUserStream ()
的第一个声明采用 String
参数。在本例中,参数表示您要搜索的用户名。这个字符串可以是子字符串,它解释了在执行搜索时的 String.contains ()
方法的选择。请注意,使用 *
表示请求所有用户的列表。该方法迭代属性文件的密钥集,委派至 getUserByUsername ()
以加载用户。请注意,我们会根据 firstResult
和 maxResults
参数对这个调用进行索引。如果您的外部存储不支持分页,则必须执行类似的逻辑。
PropertyFileUserStorageProvider
采用 Map
参数的 searchForUserStream ()
方法可以根据名字、姓氏、用户名和电子邮件搜索用户。仅存储用户名,因此搜索仅基于用户名,当 Map
参数不包含 username
属性时除外。在这种情况下,所有用户都会被返回。在这种情况下,使用 searchForUserStream (realm, search, firstResult, maxResults)
。
PropertyFileUserStorageProvider
组或属性不会存储,因此其他方法会返回一个空流。
7.8. 增加外部存储 复制链接链接已复制到粘贴板!
PropertyFileUserStorageProvider
示例真正有限。虽然我们将能够使用存储在属性文件中的用户登录,但我们将无法执行其他操作。如果此提供程序加载的用户需要特殊的角色或组映射来完全访问特定应用程序,则无法向这些用户添加额外的角色映射。您还可以修改或添加其他重要属性,如电子邮件、名字和姓氏。
对于这些类型的情况,红帽构建的 Keycloak 允许您在红帽构建的 Keycloak 数据库中存储额外信息来增加外部存储。这称为联邦用户存储,并封装在 org.keycloak.storage.federated.UserFederatedStorageProvider
类中。
UserFederatedStorageProvider
UserFederatedStorageProvider
实例在 UserStorageUtil.userFederatedStorage (KeycloakSession)
方法上可用。它具有存储属性、组和角色映射、不同凭证类型和所需操作的所有不同类型的方法。如果您的外部存储的数据型号不支持红帽构建的 Keycloak 功能集,则该服务可能会填补差距。
红帽 Keycloak 的构建附带一个帮助程序类 org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage
,它将把每个单用户 Model
方法委托给用户联邦存储。覆盖您需要覆盖的方法,以委派给您的外部存储表示法。强烈建议您阅读此类的 javadoc,因为它有较小的保护方法,您可能需要覆盖。专门围绕组成员资格和角色映射。
7.8.1. 8 月示例 复制链接链接已复制到粘贴板!
在我们的 PropertyFileUserStorageProvider
示例中,我们需要对提供商进行简单的更改才能使用 AbstractUserAdapterFederatedStorage
。
PropertyFileUserStorageProvider
我们改为定义 AbstractUserAdapterFederatedStorage
的匿名类实现。setUsername ()
方法更改属性文件并保存它。
7.9. 导入实施策略 复制链接链接已复制到粘贴板!
在实施用户存储提供程序时,您可以采用另外一种策略。您可以在 Red Hat build of 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
的实施。每个方法仅委托给 用户模型
,用它实例化。我们覆盖此委派类的 setUsername ()
方法,以与属性文件自动同步。对于供应商,您可以使用它 截获 本地 UserModel
上的其他方法,以执行与外部存储同步。例如,get 方法可以确保本地存储处于同步状态。设置方法使外部存储与本地存储保持同步。需要注意的是,getId ()
方法应始终返回您在本地创建用户时自动生成的 id。您不应该返回联邦 id,如其它非导入示例所示。
如果您的供应商实现 UserRegistrationProvider
接口,则您的 removeUser ()
方法不需要从本地存储中删除该用户。运行时会自动执行此操作。另请注意,在从本地存储中删除 removeUser ()
之前,会先调用 removeUser ()。
7.9.1. ImportedUserValidation 接口 复制链接链接已复制到粘贴板!
如果您记得在本章的前面部分,我们讨论如何查询用户工作。如果找到了用户,则会首先查询本地存储,然后查询结束。这是以上实施的一个问题,因为我们希望代理本地 UserModel
,以便我们可以使用户名保持同步。用户存储 SPI 具有回调,每当从本地数据库加载链接的本地用户时。
每当加载链接的本地用户时,如果用户存储类实施此接口,则调用 validate ()
方法。您可以在此处代理作为参数传递的本地用户并返回它。这将使用新的 UserModel
。您还可以选择检查来查看用户是否存在于外部存储中。如果 validate ()
返回 null
,则本地用户将从数据库中删除。
7.9.2. ImportSynchronization 接口 复制链接链接已复制到粘贴板!
使用导入策略时,您可以看到本地用户复制可以与外部存储不同步。例如,一个用户已从外部存储中删除。User Storage SPI 有一个额外的接口,您可以实现处理这个接口 org.keycloak.storage.user.ImportSynchronization
:
这个接口由供应商工厂实现。当此接口由供应商工厂实现后,供应商的管理控制台管理页面会显示其他选项。您可以点击按钮来手动强制同步。这会调用 ImportSynchronization.sync ()
方法。另外,会显示额外的配置选项,供您自动调度同步。自动同步调用 syncSince ()
方法。
7.10. 用户缓存 复制链接链接已复制到粘贴板!
当用户对象通过 ID、用户名或电子邮件查询加载时,会缓存它。当缓存用户对象时,它会迭代整个 UserModel
接口,并将这些信息拉取到本地的内存中缓存。在集群中,此缓存仍然是本地的,但会变得失效缓存。修改用户对象时,它将被驱除。此驱除事件被传播到整个集群,以便其他节点的用户缓存也无效。
7.10.1. 管理用户缓存 复制链接链接已复制到粘贴板!
您可以通过调用 KeycloakSession.getProvider (UserCache.class)
来访问用户缓存。
有可驱除特定用户、特定域中包含的用户或整个缓存的方法。
7.10.2. OnUserCache 回调接口 复制链接链接已复制到粘贴板!
您可能需要缓存特定于您的供应商实现的额外信息。每当用户缓存时,User Storage 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 不是应用服务器。
因此,用户存储提供程序无法打包在任何 Jakarta EE 组件中,或者使其成为 EJB,当 Keycloak 在以前的版本中通过 WildFly 运行时。
提供商实施必须是实现合适的用户存储 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
我们仅显示此 REST API 与 Java 客户端的交互。希望您可以从此 API 从 curl
中提取如何执行此操作。
要创建用户存储供应商,您必须指定 provider id、字符串 org.keycloak.storage.UserStorageProvider
的供应商类型,以及配置。
7.13. 从早期的用户联邦 SPI 迁移 复制链接链接已复制到粘贴板!
只有在您使用之前(及现已删除)用户联邦 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. 导入与非导入 复制链接链接已复制到粘贴板!
较早的用户联邦 SPI 要求您在 Red Hat build of Keycloak 的数据库中创建用户的本地副本,并将信息从外部存储导入到本地副本。但是,这不再是必需的。您仍然可以将较早的供应商移植为原样,但您应该考虑非导入策略是否为更好的方法。
导入策略的优点:
- 红帽构建的 Keycloak 基本上成为外部存储的持久性用户缓存。导入用户后,您将不会再达到外部存储,从而关闭它。
- 如果您要作为官方用户存储并弃用较早的外部存储红帽构建的 Keycloak,您可以慢慢地迁移应用程序以使用红帽构建的 Keycloak。所有应用迁移后,取消链接导入的用户,然后停用早期的传统外部存储。
使用导入策略时有一些明显的缺点:
- 第一次查找用户需要多次更新红帽构建的 Keycloak 数据库。这可能会给负载造成大量性能损失,并在红帽构建的 Keycloak 数据库中造成大量压力。用户联合存储方法将仅存储额外的数据,且可能永远不会根据外部存储的功能使用。
- 通过导入方法,您必须保留本地红帽构建的 Keycloak 存储和外部存储同步。用户存储 SPI 具有您可以实现支持同步的功能接口,但可能会快速变得困难且出现问题。
首先要注意的是,UserFederationProvider
是一个完整的接口。您在此界面中实施每个方法。但是,UserStorageProvider
会根据需要把这个接口分为多个您实现的功能接口。
UserFederationProvider.getUserByUsername ()
和 getUserByEmail ()
在新的 SPI 中具有完全对应的项。两者之间的差别在于您导入的方式。如果您要继续导入策略,您不再调用 KeycloakSession.userStorage ().addUser ()
来在本地创建用户。相反,您调用 KeycloakSession.userLocalStorage ().addUser ()
。userStorage ()
方法不再存在。
UserFederationProvider.validateAndProxy ()
方法已移至可选的功能接口 ImportedUserValidation
。如果您要将早期供应商按原样移植,则需要实施此接口。另请注意,在之前的 SPI 中,每次访问用户时都会调用此方法,即使本地用户位于缓存中。在后续的 SPI 中,只有从本地存储加载本地用户时,才会调用此方法。如果缓存本地用户,则不会调用 ImportedUserValidation.validate ()
方法。
后续 SPI 不再存在 UserFederationProvider.isValid ()
方法。
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")存在用户存储提供程序时,才会发生此迁移。
因此,您可以采取不同的方法。
- 您可以删除之前的 Red Hat build of Keycloak 部署中的供应商。这将删除您导入的所有用户的本地链接副本。然后,当您升级红帽构建的 Keycloak 时,只为您的域部署和配置新供应商。
-
第二个选项是编写新提供程序,确保它具有相同的供应商 ID:
UserStorageProviderFactory.getId ()
。确保此提供程序已部署到服务器。引导服务器,并让内置迁移脚本从较早的数据模型转换为后续的数据模型。在这种情况下,您之前所有链接的用户都可以正常工作,且相同。
如果您决定获得导入策略并重写您的用户存储供应商,我们建议您在升级 Red Hat build of Keycloak 前删除之前的供应商。这将删除您导入的任何用户的链接本地导入副本。
7.14. 基于流的接口 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 中的许多用户存储接口包含可以返回潜在大量对象的查询方法,这可能会在内存消耗和处理时间方面造成显著影响。当查询方法的逻辑中只使用对象的内部状态时,这尤其如此。
为了为开发人员提供了更有效的、处理这些查询方法中大型数据集的替代选择,已将 Streams
子接口添加到用户存储接口中。这些 Streams
子接口将超级接口中的基于原始集合的方法替换为基于流的变体,使基于集合的方法被默认。基于集合的查询方法的默认实现调用其 流
对应部分,并将结果收集到正确的集合类型。
Streams
子接口允许实现重点放在基于流的方法处理数据集,并从该方法的潜在内存和性能优化中受益。提供要实施的 Streams
子接口的接口包括几个 功能接口,org.keycloak.storage.federated
软件包中所有接口,以及可能根据自定义存储实施的范围来实施的其他接口。
请参阅此为开发人员提供 Streams
子接口的接口列表。
软件包 | 类 |
|
|
|
|
| 所有接口 |
|
|
AssumeRole 表示接口是一个 功能接口
要从流方法中受益的自定义用户存储实现应该只实现 Streams
子接口,而不是原始接口。例如,以下代码使用 UserQueryProvider
接口的 Streams
变体:
第 8 章 Vault SPI 复制链接链接已复制到粘贴板!
8.1. Vault 供应商 复制链接链接已复制到粘贴板!
您可以使用 org.keycloak.vault
软件包中的 vault SPI 为红帽构建的 Keycloak 编写自定义扩展,以连接到任意密码库实施。
内置 files-plaintext
提供程序是此 SPI 的实施示例。通常应用以下规则:
-
要防止 secret 在域间泄漏,您可能需要隔离或限制域可以检索的 secret。在这种情况下,您的供应商在查找 secret 时应考虑 realm 名称,例如,使用 realm 名称作为前缀。例如,一个表达式
${vault.key}
将通常会评估不同的条目名称,具体取决于它在 realm A 或 realm B 中使用。要区分不同的域,需要将域从VaultProvider
Factory.create ()KeycloakSession
参数获得。 -
vault 供应商需要实施单一方法 get
Secret
,它为给定 secret 名称返回VaultRawSecret
。该类包含 secret 的表示,可以是byte[]
或ByteBuffer
,并且应该在需要时在两者之间进行转换。请注意,在用法后,此缓冲区将丢弃,如下所述。
有关如何打包和部署自定义提供程序的详情,请参考 服务提供商接口 章节。
8.2. 从 vault 中消耗值 复制链接链接已复制到粘贴板!
库包含敏感数据,红帽构建的 Keycloak 会相应地对待 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
,但 String
对象中存储的 secret 至少会到下一个 GC 舍入。因此,首选使用普通字节和字符数组和缓冲区。