2.7. 弃用和删除的功能
2.7.1. truststore 弃用 复制链接链接已复制到粘贴板!
spi-truststore-file jpeg 选项和信任存储相关的选项 https-trust-store114 已被弃用。因此,对信任存储资料使用新的默认位置 conf/truststores,或使用 truststore-paths 选项指定您需要的路径。详情请参阅 为传出请求配置可信证书。
应该使用 tls-hostname-verifier 属性,而不是 spi-truststore-file-hostname-verification-policy 属性。
更改的资料效果是,信任存储供应商总是使用一些证书(至少存在默认的 Java 可信证书)。这个新行为可能会影响红帽构建的 Keycloak 的其他部分。
例如,如果 测试 机构被配置为 Direct 验证,webauthn 注册可能会失败。在以前的版本中,如果 truststore 供应商没有配置传入的证书,则不会验证传入的证书。但是现在,这个验证总是被执行。注册失败,并显示 无效的证书路径 错误,因为 dongle 发送的证书链不被红帽构建的 Keycloak 信任。验证器的证书颁发机构需要存在于信任存储提供程序中,才能正确执行 attestation。
2.7.2. 弃用的 --proxy 选项 复制链接链接已复制到粘贴板!
--proxy 选项已弃用,并将在以后的发行版本中删除。下表说明了已弃用的选项如何映射到支持的选项。
| 弃用的用法 | 新用法 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
为提高安全性,--proxy-headers 选项不允许同时选择 正向和 x 值(因为它是 forwarded --proxy edge 和 --proxy reencrypt之前的情况)。
使用 proxy 标头选项时,请确保您的反向代理正确设置并分别覆盖 Forwarded 或 X-ForwardedGalaxy 标头。要设置这些标头,请参阅您的反向代理文档。错误配置会将红帽构建的 Keycloak 暴露给安全漏洞。
您还可以使用 Operator 时设置代理标头:
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: example-kc
spec:
...
proxy:
headers: forwarded|xforwarded
如果没有指定 proxy.headers 字段,Operator 会默认隐式设置 proxy=passthrough 来回退到之前的行为。这会在服务器日志中产生弃用警告。此回退将在以后的发行版本中被删除。
2.7.3. 弃用的离线会话预加载 复制链接链接已复制到粘贴板!
红帽构建的 Keycloak 的默认行为是按需加载离线会话。在启动时预加载它们的旧行为现已弃用,因为在启动时预加载它们无法很好地扩展,并增加红帽构建的 Keycloak 内存用量。旧的行为将在以后的发行版本中被删除。
要在已弃用且还没有删除时重新启用旧行为,请使用功能标记和 SPI 选项,如下所示:
bin/kc.[sh|bat] start --features-enabled offline-session-preloading --spi-user-sessions-infinispan-preload-offline-sessions-from-database=true
UserSessionProvider 的 API 弃用了方法 getOfflineUserSessionByBrokerSessionId (RealmModel realm, String brokerSessionId)。除了此方法外,请使用 getOfflineUserSessionByBrokerUserIdStream (RealmModel, String brokerUserId) 来首先获取用户的会话,然后根据需要过滤代理会话 ID。
2.7.4. 弃用了数据供应商和模型的方法 复制链接链接已复制到粘贴板!
-
RealmModel#getTopLevelGroupsStream ()和超载方法现已弃用
2.7.5. Cookie 的弃用和删除 复制链接链接已复制到粘贴板!
作为红帽构建的 Keycloak 中重构 Cookie 处理的一部分,有一些对 Cookie 的设置方式的更改:
- 现在,如果请求通过安全上下文,则所有 Cookie 都会设置 secure 属性
-
WELCOME_STATE_CHECKERcookies 现在设置SameSite=Strict
对于自定义扩展,可能需要进行一些更改:
-
LocaleSelectorProvider.KEYCLOAK_LOCALE已被弃用,因为 Cookie 现在通过 CookieProvider 管理 -
HttpResponse.setWriteCookiesOnTransactionCompletehas been removed -
HttpCookie已被弃用,请使用NewCookie.Builder -
ServerCookie已被弃用,请使用NewCookie.Builder
2.7.6. 为 SAML 加密删除已弃用的模式 复制链接链接已复制到粘贴板!
现在,在版本 21 中引入的 SAML 加密的兼容性模式已被删除。系统属性 keycloak.saml.deprecated.encryption 不再由服务器管理。仍使用旧签名密钥进行加密的客户端应该从新的 IDP 配置元数据进行更新。
2.7.7. 重命名模型模块 复制链接链接已复制到粘贴板!
删除 Map Store 后,以下模块被重命名:
-
org.keycloak:keycloak-model-legacy-privatetoorg.keycloak:keycloak-model-storage-private -
org.keycloak:keycloak-model-legacy-servicestoorg.keycloak:keycloak-model-storage-services
和 org.keycloak:keycloak-model-legacy 模块已被弃用,并将在 org.keycloak:keycloak-model-storage 模块的下一发行版本中删除。
2.7.8. 删除了 RegistrationProfile 表单操作 复制链接链接已复制到粘贴板!
表单操作 RegistrationProfile (在身份验证流的 UI 中显示为 Profile Validation)已从代码库中删除,也可以从所有验证流中删除。默认情况下,它位于每个域的内置注册流中。验证用户属性以及创建用户,包括所有用户的属性都由 RegistrationUserCreation 表单操作处理,因此不再需要 RegistrationProfile。与这个更改相关的通常不需要进一步的操作,除非您在您自己的供应商中使用 RegistrationProfile 类。
2.7.9. 不再支持通过 Admin User API 更新用户时对用户属性进行部分更新 复制链接链接已复制到粘贴板!
通过 Admin User API 更新用户属性时,在更新用户属性时,您无法执行部分更新,包括 用户名、电子邮件、firstName 和 lastName 等 root 属性。
2.7.10. 弃用的 auto-build CLI 选项已被删除 复制链接链接已复制到粘贴板!
auto-build CLI 选项在很长时间内被标记为弃用。在这个发行版本中,它已被完全删除,它不再被支持。
在执行 start 命令时,服务器会根据配置自动构建。要防止此行为,请设置 --optimized 标志。
2.7.11. 删除选项以修剪事件的详情长度 复制链接链接已复制到粘贴板!
从这个版本开始,Keycloak 支持 EventEntity details 列的长值。因此,它不再支持修剪事件详情长度 --spi-events-store-jpa-max-detail-length 和 --spi-events-store-jpa-max-field-length 的选项。
2.7.12. 从转换中删除命名空间 复制链接链接已复制到粘贴板!
如果您编写了自己的翻译或扩展管理员 ui,则需要将它们迁移到此新格式时,我们将所有翻译移到 admin-ui 中的一个文件中。另外,如果您的数据库中有"overrides",则必须从密钥中删除命名空间。有些键只在不同的命名空间中相同,这最明显有助于。在这些情况下,我们使用 帮助 来记录密钥。
如果要使用这个节点脚本来帮助迁移。它将获取所有单个文件并将其放入一个新文件,并负责一些映射:
import { readFileSync, writeFileSync, appendFileSync } from "node:fs";
const ns = [
"common",
"common-help",
"dashboard",
"clients",
"clients-help",
"client-scopes",
"client-scopes-help",
"groups",
"realm",
"roles",
"users",
"users-help",
"sessions",
"events",
"realm-settings",
"realm-settings-help",
"authentication",
"authentication-help",
"user-federation",
"user-federation-help",
"identity-providers",
"identity-providers-help",
"dynamic",
];
const map = new Map();
const dup = [];
ns.forEach((n) => {
const rawData = readFileSync(n + ".json");
const translation = JSON.parse(rawData);
Object.entries(translation).map((e) => {
const name = e[0];
const value = e[1];
if (map.has(name) && map.get(name) !== value) {
if (n.includes("help")) {
map.set(name + "Help", value);
} else {
map.set(name, value);
dup.push({
name: name,
value: map.get(name),
dup: { ns: n, value: value },
});
}
} else {
map.set(name, value);
}
});
});
writeFileSync(
"translation.json",
JSON.stringify(Object.fromEntries(map.entries()), undefined, 2),
);
const mapping = [
["common:clientScope", "clientScopeType"],
["identity-providers:createSuccess", "createIdentityProviderSuccess"],
["identity-providers:createError", "createIdentityProviderError"],
["clients:createError", "createClientError"],
["clients:createSuccess", "createClientSuccess"],
["user-federation:createSuccess", "createUserProviderSuccess"],
["user-federation:createError", "createUserProviderError"],
["authentication-help:name", "flowNameHelp"],
["authentication-help:description", "flowDescriptionHelp"],
["clientScopes:noRoles", "noRoles-clientScope"],
["clientScopes:noRolesInstructions", "noRolesInstructions-clientScope"],
["users:noRoles", "noRoles-user"],
["users:noRolesInstructions", "noRolesInstructions-user"],
["clients:noRoles", "noRoles-client"],
["clients:noRolesInstructions", "noRolesInstructions-client"],
["groups:noRoles", "noRoles-group"],
["groups:noRolesInstructions", "noRolesInstructions-group"],
["roles:noRoles", "noRoles-roles"],
["roles:noRolesInstructions", "noRolesInstructions-roles"],
["realm:realmName:", "realmNameField"],
["client-scopes:searchFor", "searchForClientScope"],
["roles:searchFor", "searchForRoles"],
["authentication:title", "titleAuthentication"],
["events:title", "titleEvents"],
["roles:title", "titleRoles"],
["users:title", "titleUsers"],
["sessions:title", "titleSessions"],
["client-scopes:deleteConfirm", "deleteConfirmClientScopes"],
["users:deleteConfirm", "deleteConfirmUsers"],
["groups:deleteConfirm_one", "deleteConfirmGroup_one"],
["groups:deleteConfirm_other", "deleteConfirmGroup_other"],
["identity-providers:deleteConfirm", "deleteConfirmIdentityProvider"],
["realm-settings:deleteConfirm", "deleteConfirmRealmSetting"],
["roles:whoWillAppearLinkText", "whoWillAppearLinkTextRoles"],
["users:whoWillAppearLinkText", "whoWillAppearLinkTextUsers"],
["roles:whoWillAppearPopoverText", "whoWillAppearPopoverTextRoles"],
["users:whoWillAppearPopoverText", "whoWillAppearPopoverTextUsers"],
["client-scopes:deletedSuccess", "deletedSuccessClientScope"],
["identity-providers:deletedSuccess", "deletedSuccessIdentityProvider"],
["realm-settings:deleteSuccess", "deletedSuccessRealmSetting"],
["client-scopes:deleteError", "deletedErrorClientScope"],
["identity-providers:deleteError", "deletedErrorIdentityProvider"],
["realm-settings:deleteError", "deletedErrorRealmSetting"],
["realm-settings:saveSuccess", "realmSaveSuccess"],
["user-federation:saveSuccess", "userProviderSaveSuccess"],
["realm-settings:saveError", "realmSaveError"],
["user-federation:saveError", "userProviderSaveError"],
["realm-settings:validateName", "validateAttributeName"],
["identity-providers:disableConfirm", "disableConfirmIdentityProvider"],
["realm-settings:disableConfirm", "disableConfirmRealm"],
["client-scopes:updateSuccess", "updateSuccessClientScope"],
["client-scopes:updateError", "updateErrorClientScope"],
["identity-providers:updateSuccess", "updateSuccessIdentityProvider"],
["identity-providers:updateError", "updateErrorIdentityProvider"],
["user-federation:orderChangeSuccess", "orderChangeSuccessUserFed"],
["user-federation:orderChangeError", "orderChangeErrorUserFed"],
["authentication-help:alias", "authenticationAliasHelp"],
["authentication-help:flowType", "authenticationFlowTypeHelp"],
["authentication:createFlow", "authenticationCreateFlowHelp"],
["client-scopes-help:rolesScope", "clientScopesRolesScope"],
["client-scopes-help:name", "scopeNameHelp"],
["client-scopes-help:description", "scopeDescriptionHelp"],
["client-scopes-help:type", "scopeTypeHelp"],
["clients-help:description", "clientDescriptionHelp"],
["clients-help:clientType", "clientsClientTypeHelp"],
["clients-help:scopes", "clientsClientScopesHelp"],
["common:clientScope", "clientScopeTypes"],
["dashboard:realmName", "realmNameTitle"],
["common:description", "description"],
];
mapping.forEach((m) => {
const key = m[0].split(":");
try {
const data = readFileSync(key[0] + ".json");
const translation = JSON.parse(data);
const value = translation[key[1]];
if (value) {
appendFileSync(
"translation.json",
'"' + m[1] + '": ' + JSON.stringify(value) + ',\n',
);
}
} catch (error) {
console.error("skipping namespace key: " + key);
}
});
将此保存到 public/locale/<language > 文件夹中的名为 transform.mjs 的文件,并使用以下内容运行它:
node ./transform.mjs
这可能不会进行完整的转换,但非常接近。