【DevOps工具篇】Keycloak安装配置及脚本化
@TOC
推荐超级课程:
我们以脚本化的方式安装和配置Keycloak。
所示配置将使用与入门教程 中Keycloak网站相同的值。因此,我们可以直接从Keycloak网站使用单页面应用程序 (SPA)来测试我们的结果。
与大多数关于Keycloak的其他博客/教程/文章的一个重大区别是,我们通过使用Keycloak的管理CLI 显示所有配置。
我们展示了如何使用管理CLI自定义master
领域以及如何创建一个新的领域myrealm
,其中包含一个用户和一个客户端。
我们还提出了一套用于清晰和可读的配置过程的Shell脚本。在所有即将发布的教程中,将应用相同的配置Keycloak的过程。
您可以选择在安装中使用3种不同的运行时环境:
- 本地运行时中官方分发文件
keycloak-<VERSION>.[zip|tar.gz]
- Docker主机运行时中的官方Docker映像
- OpenShift运行时中的官方Docker映像
您只需要执行上述任何一个变体即可完成此场景,并为其他场景做好准备。
脚本化
以脚本化的方式进行所有配置有很多优势:
- 它允许自动化
- 它允许使用变量来支持各种环境
- 它允许幂等性(脚本可以多次执行产生相同的结果)
虽然存在其他用于脚本化配置的替代方法,但是管理CLI在所有运行时环境中都可用,并且可以直接使用。
直接使用管理REST API 是一种选择,但需要额外的自定义开发。
Keycloak操作员只能在基于Kubernetes的运行时环境中使用。
要求
为了轻松处理来自管理CLI调用结果的JSON结构,我们使用jq 工具。
步骤
步骤1:安装并启动Keycloak服务器
分发文件进行安装
让我们从下载Keycloak分发文件开始。
export KC_VERSION=21.0.2curl -LO https://github.com/keycloak/keycloak/releases/download/"${KC_VERSION}"/keycloak-"${KC_VERSION}".zip
接着解压缩存档。
unzip keycloak-${KC_VERSION}.zip
现在切换到新目录。
cd keycloak-${KC_VERSION}
该目录包含一个Keycloak Quarkus应用程序。
当我们首次启动服务器时,我们必须设置管理员用户和管理员密码:
KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=admin ./bin/kc.sh start-dev
再次启动时,无需重新设置这些变量。您可以使用以下命令启动服务器:
./bin/kc.sh start-dev
start-dev
以DEV模式运行quarkus应用程序。不要将其用于生产环境。
默认情况下,Keycloak服务器使用以下端口。它们仅从本地回环地址 127.0.0.1
提供服务:
- 8080用于使用HTTP的Keycloak
从日志输出的最后几行中可以看到:
2023-04-11 13:23:29,545 INFO [io.quarkus] (main) Keycloak 21.0.2 on JVM (powered by Quarkus 2.13.7.Final) started in 4.418s. Listening on: http://0.0.0.0:8080
现在,我们可以从localhost打开管理控制台 ,并使用刚创建的管理员用户登录。
分发文件还包含管理CLI。这是shell脚本 ./bin/kcadm.sh
。
我们为kcadm.sh
脚本定义环境变量KCADM
。它必须是上述Keycloak安装中kcadm.sh
脚本的绝对路径。
export KCADM="/path/to/keycloak/bin/kcadm.sh"
export HOST_FOR_KCADM=localhost
Docker映像进行安装
要将Keycloak安装为Docker容器,只需要一个命令。
export KC_VERSION=21.0.2docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:${KC_VERSION} start-dev
在接下来的步骤中,我们将使用管理CLI脚本(kcadm.sh
)。它也包含在Keycloak的Docker映像中。这意味着每次使用管理CLI时,都会执行来自Docker映像内部的脚本。
docker run --rm --entrypoint /opt/keycloak/bin/kcadm.sh quay.io/keycloak/keycloak:${KC_VERSION}
我们为上述命令定义环境变量KCADM
。此外,我们将docker主机上的$HOME/.keycloak
文件夹挂载到/opt/.keycloak
。
export KCADM="docker run --rm --entrypoint /opt/keycloak/bin/kcadm.sh -v ${HOME}/.keycloak:/opt/keycloak/.keycloak quay.io/keycloak/keycloak:${KC_VERSION}"
如果成功执行了$KCADM
,则会显示以下输出:
Keycloak Admin CLI
Use 'kcadm.sh config credentials' command with username and password to start a session against a specific
server and realm.
For example:
$ kcadm.sh config credentials --server http://localhost:8080/auth --realm master --user admin
Enter password:
Logging into http://localhost:8080/auth as user admin of realm master
Any configured username can be used for login, but to perform admin operations the user
needs proper roles, otherwise operations will fail.
Usage: kcadm.sh COMMAND [ARGUMENTS]
Global options:
-x Print full stack trace when exiting with error
--help Print help for specific command
--config Path to the config file (~/.keycloak/kcadm.config by default)
Commands:
config Set up credentials, and other configuration settings using the config file
create Create new resource
get Get a resource
update Update a resource
delete Delete a resource
get-roles List roles for a user or a group
add-roles Add role to a user or a group
remove-roles Remove role from a user or a group
set-password Re-set password for a user
help This help
Use 'kcadm.sh help <command>' for more information about a given command.
当使用带有命令(如配置,创建,获取等)的此脚本时,它将连接到另一个运行的Keycloak实例。根据您使用的Docker环境,必须以不同方式指定Keycloak实例的主机名。对于Docker Desktop
环境,主机名可以定义为host.docker.internal
。
export HOST_FOR_KCADM=host.docker.internal
在OpenShift内的Docker映像中安装
敬请期待!
步骤2:连接管理CLI
现在,我们将Keycloak管理CLI连接到API并使用先前创建的用户进行身份验证。我们使用两个在步骤1中创建的环境变量:
$KCADM
$HOST_FOR_KCADM
请确保它们已定义。它们的定义取决于您选择的运行时环境。
我们使用admin
用户登录到master
领域。通过使用config credentials
选项,我们请求并维护一个经过身份验证的会话,该会话用于所有后续调用。请注意,此会话的访问令牌和刷新令牌将存储在文件$HOME/.keycloak/kcadm.config
中。
$KCADM config credentials --server http://$HOST_FOR_KCADM:8080 --user admin --password admin --realm master
为了检查成功的身份验证和已身份验证的会话,我们对API进行第一次请求。
$KCADM get serverinfo
Keycloak服务器将以信息的形式响应关于其状态和功能的信息。同样的信息也可以在Web管理控制台 中找到。
步骤3:配置
我们将配置一个新的领域,包含一个新的客户端和一个新的用户。
简单配置
通过在Shell脚本中执行管理CLI命令进行Keycloak的完整配置。
出于简单起见,本节中所有CLI命令都在一个文件myrealm-keycloak-configuration.sh
中。接下来的部分显示了如何为更好地维护将脚本分组。
文件myrealm-keycloak-configuration.sh
的完整内容(下载
)
#!/usr/bin/env bash
set -euo pipefail
# authenticate and create session
$KCADM config credentials --server http://$HOST_FOR_KCADM:8080 --user admin --password admin --realm master
# define the name of the realm used as the target for the following commands
REALM_NAME=master
# enable the storage of admin events including their representation
$KCADM update events/config -r ${REALM_NAME} -s adminEventsEnabled=true -s adminEventsDetailsEnabled=true
# enable the storage of login events and define the expiration of a stored login event
$KCADM update events/config -r ${REALM_NAME} -s eventsEnabled=true -s eventsExpiration=259200
# define the login event types to be stored
$KCADM update events/config -r ${REALM_NAME} -s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
# change target realm
REALM_NAME=myrealm
# create a new realm
$KCADM create realms -s realm="${REALM_NAME}" -s enabled=true
# enable the storage of admin events including their representation
$KCADM update events/config -r ${REALM_NAME} -s adminEventsEnabled=true -s adminEventsDetailsEnabled=true
# enable the storage of login events and define the expiration of a stored login event
$KCADM update events/config -r ${REALM_NAME} -s eventsEnabled=true -s eventsExpiration=259200
# define the login event types to be stored
$KCADM update events/config -r ${REALM_NAME} -s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
# create a new client
$KCADM create clients -r ${REALM_NAME} -s clientId="myclient" -s enabled=true -s name="My Client" -s protocol=openid-connect -s publicClient=true -s standardFlowEnabled=true -s 'redirectUris=["https://www.keycloak.org/app/*"]' -s baseUrl="https://www.keycloak.org/app/" -s 'webOrigins=["*"]'
# create a new user
$KCADM create users -r $REALM_NAME -s username="myuser" -s enabled=true -s firstName="My" -s lastName="User" -s email="my.user@email.com"
# set the password for the new user
$KCADM set-password -r $REALM_NAME --username "myuser" --new-password "myuser"
随后,我们解释上述脚本中的每个CLI调用。
我们使用admin
用户登录到master
realm,并请求一个经过身份验证的会话,该会话将用于所有进一步的调用。
$KCADM config credentials --server http://localhost:8080/auth --user admin --password admin --realm master
admin
用户可以访问所有领域。因此,我们需要指定每个命令应该应用到哪个领域。这可以通过-r
选项,后跟领域名称来完成。我们为在所有后续调用中与-r
选项一起使用的领域名称定义一个变量。
REALM_NAME=master
Keycloak知道丰富的事件集。它们被分为admin和login事件。因为Keycloak默认不存储这些事件,因此不能在管理控制台中查看。我们首先启用存储admin事件及其详细信息。
$KCADM update events/config -r ${REALM_NAME} -s adminEventsEnabled=true -s adminEventsDetailsEnabled=true
我们还启用了登录事件的存储,并将存储的事件的过期期限设置为3天(以秒259200
计)。
$KCADM update events/config -r ${REALM_NAME} -s eventsEnabled=true -s eventsExpiration=259200
我们定义了要存储的登录事件类型列表。获取完整的登录事件类型列表的最简单方法是从serverinfo
获取。
$KCADM update events/config -r ${REALM_NAME} -s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
因为Keycloak建议
master
realm应该仅用于其他领域的管理目的,我们创建一个名为myrealm
的新领域。
REALM_NAME=myrealm$KCADM create realms -s realm="${REALM_NAME}" -s enabled=true
我们为我们的新领域配置与master
realm相同的事件存储。
$KCADM update events/config -r ${REALM_NAME} -s adminEventsEnabled=true -s adminEventsDetailsEnabled=true$KCADM update events/config -r ${REALM_NAME} -s eventsEnabled=true -s eventsExpiration=259200$KCADM update events/config -r ${REALM_NAME} -s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
我们创建一个新的客户端。请注意,在Keycloak中,客户端有两个非常相似的属性:id
和clientId
。clientId
必须在CLI调用中提供,而id
由Keycloak生成并在客户端创建后写入输出。在其他CLI调用中,必须使用id
或clientId
中的一个。
$KCADM create clients -r ${REALM_NAME} -s clientId="myclient" -s enabled=true -s name="My Client" -s protocol=openid-connect -s publicClient=true -s standardFlowEnabled=true -s 'redirectUris=["https://www.keycloak.org/app/*"]' -s baseUrl="https://www.keycloak.org/app/" -s 'webOrigins=["*"]'
我们使用用户名myuser
创建一个新用户。
$KCADM create users -r $REALM_NAME -s username="myuser" -s enabled=true -s firstName="My" -s lastName="User" -s email="my.user@email.com"
我们为新用户设置密码myuser
。
$KCADM set-password -r $REALM_NAME --username "myuser" --new-password "myuser"
当我们执行[myrealm-keycloak-configuration.sh]脚本以启动一个全新的Keycloak实例时,它将运行并以完全自动化的方式进行设置。
您应该看到类似于以下的输出:
以管理员身份登录到 http://localhost:8080/auth,使用realm master的用户 admin
创建新的带有ID'myrealm'的realm
创建具有ID'a5e95d8c-2dc0-4e42-a48e-f7873944b3eb'的新client
创建具有ID'559905a6-77bc-49ae-acdb-2788cd4282c4'的新用户
成功执行脚本后,可以测试配置。最简单的方法是使用Keycloak网站上的测试应用
。这是一个具有可配置参数的单页应用(SPA),可供Keycloak服务器使用。Keycloak URL
,Realm
和Client
的预定义参数非常适合我们的配置。
屏幕流程可通过以下截图查看:SPA Config → SPA Sign in → Keycloak Login → SPA Hello 。
在另一个教程中,我们将解释在使用此示例应用程序时发生的后台详细步骤。
然而,这个脚本远未准备好用于生产环境,不仅因为如果已经有一个名为myrealm
的领域它将失败。如果您想再次执行此脚本,需要删除myrealm
realm。这可以在管理控制台中完成,也可以通过CLI调用完成。
$KCADM delete realms/myrealm
请注意,删除领域将同时删除该领域中的用户。这就是为什么这个操作对于生产环境不是一个好主意的原因。
生产配置
为了更好地维护,我们建议重构上述脚本。
- 我们将单个脚本拆分成多个具有更清晰职责的脚本
- 我们将创建CLI调用分为
create
和update
调用,以使脚本具有幂等性 - 我们创建bash函数,使脚本更加易读
我们将从上一节的脚本分割成5个不同的脚本
keycloak-configuration.sh
脚本是Keycloak配置的入口点。只有该脚本必须启动。它使用环境变量KEYCLOAK_USER
和KEYCLOAK_PASSWORD
处理身份验证,并通过使用环境变量KEYCLOAK_HOME
为kcadm.sh
shell脚本定义KCADM
变量。KCADM
变量可以在其他脚本中使用。
keycloak-configuration-helpers.sh
脚本包含所有可重复使用的Admin CLI的bash函数。它由keycloak-configuration.sh
脚本源。主要将create
CLI调用定义为一个函数,它在执行调用之前检查存在。这样我们可以确保整个配置的幂等性。作为一般模式,我们尝试尽可能简化create
CLI调用,并使用update
CLI调用进行所有其他配置。为了更容易处理,create
CLI调用应始终输出已创建或已存在的资源的id
供后续使用。
realms.sh
脚本会源对应的shell脚本,例如realm_master.sh
和realm_myrealm.sh
用于配置每个领域。它又由keycloak-configuration.sh
脚本源。
realm_master.sh
脚本包含master
领域的所有配置。
realm_myrealm.sh
脚本包含myrealm
领域的所有配置。
让我们看看每个文件。结果配置与前一节中的相同。
keycloak-configuration.sh
这个脚本是Keycloak配置的入口点。
keycloak-configuration.sh
(download
)
#!/usr/bin/env bash
set -euo pipefail
echo ""
echo "========================================================"
echo "== STARTING KEYCLOAK CONFIGURATION =="
echo "========================================================"
BASEDIR=$(dirname "$0")
source $BASEDIR/keycloak-configuration-helpers.sh
if [ "$KCADM" == "" ]; then
KCADM=$KEYCLOAK_HOME/bin/kcadm.sh
echo "Using $KCADM as the admin CLI."
fi
$KCADM config credentials --server http://$HOST_FOR_KCADM:8080 --user $KEYCLOAK_USER --password $KEYCLOAK_PASSWORD --realm master
source $BASEDIR/realms.sh
echo "========================================================"
echo "== KEYCLOAK CONFIGURATION DONE =="
echo "========================================================"
echo ""
这个文件很少改变。
keycloak-configuration-helpers.sh
这个脚本包含所有可重复使用的Admin CLI的bash函数。它由keycloak-configuration.sh 脚本源。
keycloak-configuration-helpers.sh
(download
)
#!/usr/bin/env bash
set -euo pipefail
# 新领域也设置为启用状态
createRealm() {
# 参数
REALM_NAME=$1
#
EXISTING_REALM=$($KCADM get realms | jq '.[] | select(.realm==("'$REALM_NAME'")) | .id')
if [ "$EXISTING_REALM" == "" ]; then
$KCADM create realms -s realm="${REALM_NAME}" -s enabled=true
fi
}
# 新客户端也设置为启用状态
createClient() {
# 参数
REALM_NAME=$1
CLIENT_ID=$2
#
ID=$(getClient $REALM_NAME $CLIENT_ID)
if [[ "$ID" == "" ]]; then
$KCADM create clients -r $REALM_NAME -s clientId=$CLIENT_ID -s enabled=true
fi
echo $(getClient $REALM_NAME $CLIENT_ID)
}
# 获取给定clientId的客户端的对象ID
getClient () {
# 参数
REALM_NAME=$1
CLIENT_ID=$2
#
ID=$($KCADM get clients -r $REALM_NAME --fields id,clientId | jq '.[] | select(.clientId==("'$CLIENT_ID'")) | .id')
echo $(sed -e 's/"//g' <<< $ID)
}
# 如果尚不存在,为给定用户名创建用户并返回对象ID
createUser() {
# 参数
REALM_NAME=$1
USER_NAME=$2
#
USER_ID=$(getUser $REALM_NAME $USER_NAME)
if [ "$USER_ID" == "" ]; then
$KCADM create users -r $REALM_NAME -s username=$USER_NAME -s enabled=true
fi
echo $(getUser $REALM_NAME $USER_NAME)
}
# 给定用户名的用户的对象ID
getUser() {
# 参数
REALM_NAME=$1
USERNAME=$2
#
USER=$($KCADM get users -r $REALM_NAME -q username=$USERNAME | jq '.[] | select(.username==("'$USERNAME'")) | .id' )
echo $(sed -e 's/"//g' <<< $USER)
}
这个文件在需要添加新功能时会改变。
realms.sh
#!/usr/bin/env bash
set -euo pipefail
######################
### Setup realms via their script
######################
echo "setting up realms..."
source $BASEDIR/realm_master.sh
source $BASEDIR/realm_myrealm.sh
realm_master.sh
#!/usr/bin/env bash
set -euo pipefail
REALM_NAME='master'
echo ""
echo "================================="
echo "setting up realm $REALM_NAME..."
echo "================================="
echo ""
# enable the storage of admin events including their representation
$KCADM update events/config -r ${REALM_NAME} -s adminEventsEnabled=true -s adminEventsDetailsEnabled=true
# enable the storage of login events and define the expiration of a stored login event
$KCADM update events/config -r ${REALM_NAME} -s eventsEnabled=true -s eventsExpiration=259200
# define the login event types to be stored
$KCADM update events/config -r ${REALM_NAME} -s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
realm_myrealm.sh
#!/usr/bin/env bash
set -euo pipefail
REALM_NAME='myrealm'
echo ""
echo "================================="
echo "setting up realm $REALM_NAME..."
echo "================================="
echo ""
createRealm $REALM_NAME
# enable the storage of admin events including their representation
$KCADM update events/config -r ${REALM_NAME} -s adminEventsEnabled=true -s adminEventsDetailsEnabled=true
# enable the storage of login events and define the expiration of a stored login event
$KCADM update events/config -r ${REALM_NAME} -s eventsEnabled=true -s eventsExpiration=259200
# define the login event types to be stored
$KCADM update events/config -r ${REALM_NAME} -s 'enabledEventTypes=["CLIENT_DELETE", "CLIENT_DELETE_ERROR", "CLIENT_INFO", "CLIENT_INFO_ERROR", "CLIENT_INITIATED_ACCOUNT_LINKING", "CLIENT_INITIATED_ACCOUNT_LINKING_ERROR", "CLIENT_LOGIN", "CLIENT_LOGIN_ERROR", "CLIENT_REGISTER", "CLIENT_REGISTER_ERROR", "CLIENT_UPDATE", "CLIENT_UPDATE_ERROR", "CODE_TO_TOKEN", "CODE_TO_TOKEN_ERROR", "CUSTOM_REQUIRED_ACTION", "CUSTOM_REQUIRED_ACTION_ERROR", "EXECUTE_ACTIONS", "EXECUTE_ACTIONS_ERROR", "EXECUTE_ACTION_TOKEN", "EXECUTE_ACTION_TOKEN_ERROR", "FEDERATED_IDENTITY_LINK", "FEDERATED_IDENTITY_LINK_ERROR", "GRANT_CONSENT", "GRANT_CONSENT_ERROR", "IDENTITY_PROVIDER_FIRST_LOGIN", "IDENTITY_PROVIDER_FIRST_LOGIN_ERROR", "IDENTITY_PROVIDER_LINK_ACCOUNT", "IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR", "IDENTITY_PROVIDER_LOGIN", "IDENTITY_PROVIDER_LOGIN_ERROR", "IDENTITY_PROVIDER_POST_LOGIN", "IDENTITY_PROVIDER_POST_LOGIN_ERROR", "IDENTITY_PROVIDER_RESPONSE", "IDENTITY_PROVIDER_RESPONSE_ERROR", "IDENTITY_PROVIDER_RETRIEVE_TOKEN", "IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR", "IMPERSONATE", "IMPERSONATE_ERROR", "INTROSPECT_TOKEN", "INTROSPECT_TOKEN_ERROR", "INVALID_SIGNATURE", "INVALID_SIGNATURE_ERROR", "LOGIN", "LOGIN_ERROR", "LOGOUT", "LOGOUT_ERROR", "PERMISSION_TOKEN", "PERMISSION_TOKEN_ERROR", "REFRESH_TOKEN", "REFRESH_TOKEN_ERROR", "REGISTER", "REGISTER_ERROR", "REGISTER_NODE", "REGISTER_NODE_ERROR", "REMOVE_FEDERATED_IDENTITY", "REMOVE_FEDERATED_IDENTITY_ERROR", "REMOVE_TOTP", "REMOVE_TOTP_ERROR", "RESET_PASSWORD", "RESET_PASSWORD_ERROR", "RESTART_AUTHENTICATION", "RESTART_AUTHENTICATION_ERROR", "REVOKE_GRANT", "REVOKE_GRANT_ERROR", "SEND_IDENTITY_PROVIDER_LINK", "SEND_IDENTITY_PROVIDER_LINK_ERROR", "SEND_RESET_PASSWORD", "SEND_RESET_PASSWORD_ERROR", "SEND_VERIFY_EMAIL", "SEND_VERIFY_EMAIL_ERROR", "TOKEN_EXCHANGE", "TOKEN_EXCHANGE_ERROR", "UNREGISTER_NODE", "UNREGISTER_NODE_ERROR", "UPDATE_CONSENT", "UPDATE_CONSENT_ERROR", "UPDATE_EMAIL", "UPDATE_EMAIL_ERROR", "UPDATE_PASSWORD", "UPDATE_PASSWORD_ERROR", "UPDATE_PROFILE", "UPDATE_PROFILE_ERROR", "UPDATE_TOTP", "UPDATE_TOTP_ERROR", "USER_INFO_REQUEST", "USER_INFO_REQUEST_ERROR", "VALIDATE_ACCESS_TOKEN", "VALIDATE_ACCESS_TOKEN_ERROR", "VERIFY_EMAIL", "VERIFY_EMAIL_ERROR"]'
# clients
CLIENT_ID=myclient
ID=$(createClient $REALM_NAME $CLIENT_ID)
$KCADM update clients/$ID -r $REALM_NAME -s name="My Client" -s protocol=openid-connect -s publicClient=true -s standardFlowEnabled=true -s 'redirectUris=["https://www.keycloak.org/app/*"]' -s baseUrl="https://www.keycloak.org/app/" -s 'webOrigins=["*"]'
#users
USER_NAME=myuser
USER_ID=$(createUser $REALM_NAME $USER_NAME)
$KCADM update users/$USER_ID -r $REALM_NAME -s firstName="My" -s lastName="User" -s email="my.user@email.com"
$KCADM set-password -r $REALM_NAME --username $USER_NAME --new-password $USER_NAME