将应用程序与 Keycloak 集成
到目前为止,您已经了解了 Keycloak 中的主要概念和配置选项。在本章中,您将学习如何应用它们,以便您可以配置您的应用程序并将它们与 Keycloak 集成。
通过选定的集成场景和编码示例,您将根据应用程序使用的技术堆栈和它们运行的平台,了解哪种集成方法最适合您。您将看到使用 Go、Java、JavaScript 和 Node.js 的应用程序的不同集成选项。如果这些选项都不适合您,那么请不要担心 - 您将学习如何使用位于应用程序前面的反向代理与 Keycloak 集成。
在本章中,请记住 Keycloak 对此处介绍的集成及其实现方式并不固执己见。重点是向您展示如何将 Keycloak 集成到您的应用程序中,只要您使用支持 OpenID Connect 的语言或库。
在本章结束时,您将很好地了解一些可用的集成选项及其如何应用于您的应用程序及其运行时,以及如果提供的所有选项都不适合您,您应该考虑什么,并且您需要考虑其他选项。
在本章中,我们将介绍以下主题和集成:
-
选择集成架构
-
选择集成选项
-
与 Golang 应用程序集成
-
与 Java 应用程序集成
-
与 JavaScript 应用程序集成
-
与 Node.js 应用程序集成
-
使用反向代理
-
尽量不要实现您自己的集成
技术要求
本章的示例代码可以在与本书关联的 GitHub 存储库中找到。
如果您已安装 Git,则可以通过在终端中运行以下命令来克隆存储库:
$ git clone https://github.com/PacktPublishing/Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition.git
或者,您可以从 https://github 下载存储库的 ZIP 文件。com/PacktPublishing/Keycloak—Identity-and-Access-Management- for-Modern-Applications-2nd-Edition/archive/refs/heads/main.zip.
克隆或解压缩存储库后,请查看 ch7 目录,这是所有示例所在的位置。
在我们开始之前,你需要在不同的端口上运行 Keycloak。为此,请启动服务器,如下所示:
$ docker run -it -p 8180:8180 \
-e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:22.0.0 \
start-dev --http-port=8180
我们在不同的端口上运行 Keycloak,因为我们即将运行的示例应用程序将侦听端口 8080,这是 Keycloak 使用的默认端口。服务器应在 http://localhost:8180 可用。
现在服务器已启动,请创建一个名为 myrealm 的新领域。
由于我们要将 Keycloak 与不同类型的应用程序集成,因此我们需要在 mydomain 领域为每个应用程序创建一个客户端。
让我们从创建 myBrowserapp 客户端开始,我们将使用它来保护基于浏览器的应用程序:
- Client ID: mybrowserapp
- Root URL: http://localhost:8080
- Valid Redirect URI: /*
- Web origins: +
myBrowserapp 客户端是一个在浏览器上运行的公共客户端,无法安全地存储其凭据。您可以将其视为将单页应用程序(SPA)与 Keycloak 集成所需的最低配置。请注意,我们还需要设置 Web 来源字段以允许来自基于浏览器的应用程序的跨域请求,因为它们作为 Keycloak 服务器在不同的域或 URL 中运行。通过设置 +,我们允许来自先前设置的任何有效重定向 URI 的任何请求。
为了保护服务器端 Web 应用程序,我们将使用 mywebapp 客户端:
- Client ID: mywebapp
- Client authentication: ON
- Root URL: http://localhost:8080
- Valid Redirect URI: /*
mywebapp 客户端是一个机密客户端,当它们在服务器端运行时,可以安全地存储其凭据。当您的应用程序在后端运行时,您可以将其视为与 Keycloak 集成的最低配置,它可以通过依赖浏览器级保护机制对请求进行身份验证来自行处理身份验证流程 —— 例如,基于 cookie 的身份验证。服务于前端和后端的应用程序都可以使用此配置。
让我们创建将用于保护后端应用程序的客户端:
- Client ID: mybackend
- Client authentication: ON
- Direct Access Grants: ON
- Root URL: http://localhost:8080
正如您将在后面的章节中看到的,应该为客户端禁用 Direct Access Grants 选项。为了简单起见,我们在示例中使用它来代表用户直接从令牌端点获取令牌。此选项基本上是启用 OAuth2 资源所有者密码凭证授予类型,这现在被认为是一种不良做法。有关更多详细信息,请查看https://datatracker.ietf.org/doc/html/draft-ietfoauth-security-topics的 OAuth2 安全最佳当前实践。
mybackend 客户端是充当资源服务器的机密客户端。其目的是授权基于bearer token的请求,以允许访问其受保护的资源。
我们要创建的最后一个客户端将由在应用程序前面运行的反向代理使用。使用以下设置创建客户端:
- Client ID: proxy-client
- Client authentication: ON
- Root URL: http://localhost
- Valid Redirect URI: /*
代理客户端是一个机密客户端,反向代理将使用它来验证有关主题的信息并将其转发到其后端服务。
最后,在 Keycloak 中创建一个用户:
- Username: alice
- Password: alice
无论您在应用程序中使用何种技术,由于 Keycloak 的互操作性,您刚刚创建的配置都不会改变。正如您将在以下部分中看到的,我们正在使用不同语言和库的示例应用程序中重用相同的客户端配置。
在深入研究不同的集成和示例之前,让我们了解它们如何分为两种主要的架构风格,以及它们如何影响您的应用程序与 Keycloak 的集成方式。
查看以下链接以查看 Code in Action 视频:https://packt.link/rEPXu
选择集成架构
有两种主要的集成样式,具体取决于集成代码和配置的位置:
- 嵌入式
- 代理
嵌入到您的应用程序中的集成通常由第三方库、框架、Web 容器或应用程序服务器提供。在这种风格中,您的应用程序直接与 Keycloak 对话,并负责处理 OpenID Connect 请求和响应。使用这种风格的应用程序通常需要实现一些代码或提供某种形式的配置来启用对 OpenID Connect 的支持。您需要更改的任何设置都将要求您重新部署您的应用程序:
图 7.1:嵌入式集成风格
另一方面,在代理风格中,在你的应用程序和 Keycloak 之间存在一层间接层。集成由在你的应用程序前面运行的服务管理,并负责代表你的应用程序处理 OpenID Connect 请求和响应。因此,你的应用程序依赖 HTTP 头来获取令牌或与请求相关的任何其他安全相关数据。集成代码和配置在你的应用程序边界之外,并通过外部服务进行管理。
图 7.2:代理集成样式
在选择最佳集成风格时没有经验法则。有时,你可能会受到限制而不得不使用特定的一种。不过,它们并非相互排斥,在你的应用程序生态系统中同时拥有这两种风格是完全可行的。
如果您无法控制应用程序代码(例如,遗留代码),或者如果您的应用程序位于反向代理或 API 网关后面,并且您希望利用其功能,代理样式非常适合。它还使您能够从一个地方控制和管理与 Keycloak 的集成。
另一方面,将集成嵌入到代码中更简单,因为它不需要管理外部服务,让您可以更好地控制集成。您的应用程序是自包含的,如果您使用的框架或库为 OpenID Connect 提供了良好的支持,集成通常只需编写几行代码或提供一些配置文件。
您还应该考虑查看基于浏览器的应用程序的 OAuth 2.0(https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps),了解可用于在应用程序中有效使用 OpenID Connect 的不同应用程序架构模式。
在本节中,您了解了可用于与 Keycloak 集成的两种主要架构样式。
在下一节中,我们将根据所介绍的样式介绍不同的集成选项。
选择集成选项
除了在我们刚刚提到的两种架构风格之间进行选择之外,我们还应该了解在您的应用程序中启用 OpenID Connect 时的一些关键点。
OpenID Connect 有相当多的客户端实现,有时,您可能会发现很难决定哪一个更适合您。如果这里建议的选项都不适合您,请务必了解如何选择替代方案。
根据经验,良好集成的决定应基于符合以下要求的实现:
-
广泛采用、积极维护并得到强大的开发人员社区的支持。
-
了解最新版本的 OAuth2 和 OpenID Connect 规范。
-
与 OAuth2 和 OpenID Connect 的安全最佳实践保持一致。
-
良好的用户体验、简单的配置和简单的部署模型。
尽可能向开发人员隐藏详细信息,同时仍然提供良好的默认值,以使您的应用程序与安全最佳实践保持一致。
避免供应商锁定,并尽可能使您的应用程序符合 OAuth2 和 OpenID Connect。Keycloak 可以与任何符合这些规范的客户端集成。
理想情况下,您应该使用部署应用程序的技术堆栈和平台中免费提供的任何东西。
您还可以考虑查看 OpenID Connect 网站以获取经过认证的实现列表。该列表可在https://openid.net/developers/certified/.
在下一节中,我们将了解如何使用不同的技术堆栈集成 Keycloak。
本章提供的代码示例并不针对在生产中运行;相反,它们演示了如何使用不同的语言和库集成 Keycloak。我们建议遵循相应语言或库的说明,以便在生产中安全使用。
与 Golang 应用程序集成
Go 应用程序可以使用您喜欢的任何库与 Keycloak 集成,只要它符合 OpenID Connect 或 OAuth2 规范。
为了简单起见,并提供一个如何与 Keycloak 集成的通用示例,我们将使用https://github.com/coreos/go-oidc包。本节的代码示例可在以下目录中找到:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/go
在前面的目录中,您将找到一个 main.go 文件,其中包含运行示例所需的所有代码。在此文件中,您将看到一个 createConfig 函数声明与 Keycloak 集成所需的所有配置:
func createConfig(provider oidc.Provider) (oidc.Config, oauth2.Config) {
oidcConfig := &oidc.Config{
ClientID: "mywebapp",
}
config := oauth2.Config{
ClientID: oidcConfig.ClientID,
ClientSecret: "CLIENT_SECRET",
Endpoint: provider.Endpoint(),
RedirectURL: "http://localhost:8080/auth/callback",
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
return *oidcConfig, config
}
您应该使用 Keycloak 为 mywebapp 客户端生成的机密更改上述函数中对 CLIENT_SECRET 的引用。为此,导航到 Keycloak 管理控制台中的 mywebapp 客户端详情页,然后单击凭据选项卡。客户端机密应可从此选项卡的客户端机密字段中获得。
让我们通过在项目的根目录中运行以下命令来启动应用程序:
$ go run main.go
您的应用程序应该在http://localhost:8080启动并可用。现在,尝试访问该 URL 并使用我们在本章开头创建的用户凭据登录 Keycloak。
如果集成工作正常,您应该被重定向到 Keycloak 进行身份验证。提供用户凭据后,您应该被重定向回应用程序,现在是经过身份验证的用户,然后将出现一个页面,其中包含服务器颁发的令牌。
在本节中,您了解了如何将 Go 应用程序与 Keycloak 集成的基础知识。go-oidc 包是一个众所周知的包,它为客户端应用程序提供 OpenID Connect 功能。它为与 Keycloak 集成提供了良好的基线,并允许您为应用程序启用身份验证。
在本书编写时,go-oidc 还没有支持使用代码交换证明密钥(PKCE)来防止跨站请求伪造(CSRF)和授权代码注入攻击。这就是向 Keycloak 发出授权请求时使用 state 参数的原因。
还有相当多的第三方库是针对与 Keycloak 集成的。不幸的是,我们不能推荐它们中的任何一个,因为它们不符合本章开头提到的一些建议 —— 主要是因为它们没有得到强大社区的支持,而是得到了个人的支持。
在下一节中,我们将了解如何将 Keycloak 与 Java 应用程序集成。
与 Java 应用程序集成
框架、Web 容器和应用程序服务器作为其产品的一部分提供对 OpenID Connect 和 OAuth2 的支持,应该会让您的生活轻松得多,因为集成已经可用于您的应用程序,并且无需添加任何其他依赖项。
利用技术堆栈中已有的内容通常是最佳选择。
在接下来的几节中,我们将基于最常见的 Java 框架来研究不同的集成选项。
使用 Quarkus
Quarkus 提供了一个兼容 OpenID Connect 的扩展,称为 Quarkus-oidc。它提供了一个简单而丰富的配置模型,可以保护前端和后端应用程序。Quarkus 内置了对最常见的集成开发环境(IDE)的支持,例如 IntelliJ 和 Eclipse,您应该能够快速创建或配置现有项目,以便将其与 Keycloak 集成。
如果您是 Quarkus 新手,或者只是想使用 OpenID Connect 和 Keycloak 保护您的应用程序,请查看https://quarkus.io/指南中提供的指南,这些指南和代码示例中的大多数使用 Keycloak 作为 OpenID 提供程序,将帮助您快速入门。搜索使用 OpenID Connect 作为关键字的指南,以过滤与 Keycloak 集成相关的所有可用指南。
总之,QUKUS-OIDC 扩展允许您保护两种主要类型的应用程序:web-app 和 service。
web-app 类型表示通过浏览器使用 Keycloak 进行身份验证的应用程序,使用授权码授予类型。这些通常是前端应用程序。
另一方面,服务类型表示依赖 Keycloak 服务器颁发的不记名令牌来授权访问其受保护资源的应用程序。这些通常是后端应用程序,为前端应用程序提供某种 API。
要在项目中使用 QUKUS-oidc 扩展,请将以下依赖项添加到应用程序的 pom. xml 文件中:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
本节的代码示例可在本书的 GitHub 存储库中通过以下链接获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/quarku
在前面的目录中,您将找到一个前端目录和一个后端目录,它们都包含您需要遵循和运行即将到来的示例的所有代码。
在下一节中,我们将开始研究如何保护 Quarkus Web 应用程序以使用 Keycloak 对用户进行身份验证。
创建 Quarkus 客户端
在本节中,我们将查看以下目录中提供的代码示例:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/quarku
让我们首先配置一个 web-app 应用程序,将以下属性添加到 src/main/Resources/application.properties 文件:
quarkus.oidc.auth-server-url=http://localhost:8180/ realms/myrealm
quarkus.oidc.client-id=mywebapp
quarkus.oidc.client-secret=CLIENT_SECRET
quarkus.oidc.application-type=web-app
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
从配置的角度来看,主要的配置选项如下:
-
quarkus.oidc.auth-server-url:定义应用程序应从中获取 OpenID Connect Discovery 文档的 URL。
-
quarkus.oidc.client-id:将 Keycloak 中的客户端与此应用程序映射。对于这个应用程序,我们将使用我们在本章开头创建的 mywebapp 客户端。
-
quarkus.oidc.client-key:是 Keycloak 在创建客户端时生成的密钥。
-
quarkus.oidc.application-type:定义此应用程序是 Web 应用程序。
-
quarkus.http.auth.permission.authenticated.paths.path 和 quarkus.http.auth.permission.authenticated.policy:定义应用程序中的所有路径都需要经过身份验证的用户。
您应该使用 Keycloak 为 mywebapp 客户端生成的机密更改上述配置中对 CLIENT_SECRET 的引用。为此,导航到 Keycloak 管理控制台中的 mywebapp 客户端详情页,然后单击凭据选项卡。客户端机密应可从此选项卡的客户端机密字段中获得。
让我们通过在项目的根目录中运行以下命令来启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition /ch7/quarkus/frontend
$ ./mvnw quarkus:dev
您的应用程序应该在http://localhost:8080启动并可用。尝试访问该URL 并使用我们在本章开头创建的用户凭据登录 Keycloak。
如果集成正常工作,您应该被重定向到 Keycloak 进行身份验证。提供必要的用户凭据后,您应该被重定向回应用程序,现在作为经过身份验证的用户。
默认情况下,Quarkus 将设置一个 cookie,该 cookie 将根据 Keycloak 颁发的令牌的过期时间过期。如果您遇到用户没有被重定向到 Keycloak 进行身份验证,您可能需要清除浏览器 cookie。此行为是您可以配置的。有关更多详细信息,请查看 Quarkusoidc 扩展留档。
在本节中,您学习了如何配置 Web 应用程序以使用 Keycloak 对用户进行身份验证。此时,您应该能够创建自己的应用程序或配置现有应用程序以使用 Keycloak 对用户进行身份验证。有关详细信息,请查看扩展程序的留档https://quarkus.io/guides/security-oidc-code-flow-authenticationconcept。
在下一节中,我们将了解如何配置后端应用程序以根据 Keycloak 颁发的令牌授权对资源的访问。
创建 Quarkus 资源服务器
本节将介绍的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/quarkus/backen
对于使用 OAuth2 承载令牌进行保护的后端应用程序,其配置与配置前端应用程序类似,只是要将 quarkus.oidc.application-type 更改为 service,以及更改 quarkus.oidc.client-id 属性,以便它在 Keycloak 中映射到不同的客户端
quarkus.oidc.auth-server-url=http://localhost:8180/realms/myrealm
quarkus.oidc.client-id=mybackend
quarkus.oidc.application-type=service
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
quarkus.oidc.application-type 属性,现在设置为 service,表明此应用程序应基于 bearer tokens 授权访问。
让我们通过在项目的根目录中运行以下命令来启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/quarkus/backen
$ ./mvnw quarkus:dev
你的应用程序应该启动并在 http://localhost:8080 上可用。要访问正在运行的应用程序中的资源,你将需要一个访问令牌。要获取一个访问令牌,请使用以下命令
$ export access_token=$(\
curl -X POST http://localhost:8180/realms/myrealm/protocol/openid-connect/token \
-d "client_id=mybackend&client_secret=CLIENT_SECRET" \
-H "content-type: application/x-www-form-urlencoded" \
-d "username=alice&password=alice&grant_type=password" \
| jq --raw-output ".access_token" \
)
一旦你运行了这个命令,一个访问令牌将被保存在一个名为 access_token 的环境变量中,现在你就可以访问该应用程序了。
$ curl -X GET http://localhost:8080/hello -H "Authorization: Bearer "$access_token
因此,你应该期望从该命令中得到以下输出:
$ Hello RESTEasy
现在,如果你在没有令牌或使用无效令牌的情况下尝试访问应用程序,你应该会得到一个 401 状态码,表示你的请求被禁止:
$ curl -v -X GET http://localhost:8080/hello
quarkus-oidc 扩展根据令牌是否代表 JSON Web Token(JWT)来验证令牌。如果令牌是 JWT,扩展将尝试通过检查其签名、受众和到期日期在本地验证令牌。否则,如果令牌不透明且格式未知,它将在 Keycloak 调用令牌的自省端点来验证它。
对于 Quarkus 应用程序,quarkus-oidc 扩展是您拥有的最佳选择。它提供了一个非常简单的配置,同时提供了许多其他选项,您可以使用它们来自定义其行为。
我们仅在此处介绍了设置 quarkus-oidc 扩展的主要步骤,以便您可以通过 Keycloak 对用户进行身份验证。
您可以使用此扩展做更多事情,例如利用注销功能、将有关主题的信息获取到您的 bean、多租户等。有关详细信息,请查看扩展的留档https://quarkus.io/guides/securityoidc-bearer-authentication-concept。
在下一节中,我们将了解如何与 Spring Boot 应用程序集成。
使用 Spring Boot
Spring Boot 应用程序可以通过利用 Spring Security 的 OAuth2/OpenID 库与 Keycloak 集成。
有两个主要库,每个库都针对特定类型的应用程序:客户端和资源服务器。
本节中的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/springboot
在前面的目录中,您将找到一个前端目录和一个后端目录,其中包含您需要遵循和运行示例的所有代码。
在下一节中,我们将开始研究如何启用 Web 应用程序,以便我们可以使用 Keycloak 对用户进行身份验证。
创建 Spring Boot 客户端
本节中提供的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/springboot/frontend
此应用程序的配置可从 src/main/Resources/application 获得yaml 文件:
spring:
security:
oauth2:
client:
registration:
myfrontend:
provider: keycloak
client-id: mywebapp
client-secret: CLIENT_SECRET
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/"
scope: openid
provider:
keycloak:
issuer-uri: http://localhost:8180/realms/myrealm
如您所见,配置非常简单。我们基本上是设置在本章开头创建的客户端配置以及 Keycloak 中 myrealm 领域的 URL,以便 Spring 可以获取 OpenID Connect Discovery 文档(https://www. keycloak.org/docs/latest/securing_apps/#endpoints)来发现服务器端点和相关元数据。
您应该将上述配置中对 CLIENT_SECRET 的引用更改为 Keycloak 为 mywebapp 客户端生成的密钥。为此,请导航到 Keycloak 管理控制台中的 mywebapp 客户端详细信息页面,然后单击 Credentials 选项卡。客户端密钥应可从此选项卡的 Client secret (客户端密钥) 字段中获得。
让我们通过在项目的根目录中运行以下命令来启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/springboot/frontend
$ ./mvnw spring-boot:run
您的应用程序应在 http://localhost:8080 启动并可用。尝试访问该 URL 并使用我们在本章开头创建的用户的凭据登录到 Keycloak。
如果集成工作正常,您应该被重定向到 Keycloak 进行身份验证。提供用户凭证后,您应该被重定向回应用程序,现在作为经过身份验证的用户。
在本节中,您学习了如何配置 Web 应用程序以使用 Keycloak 对用户进行身份验证。这样,您应该能够创建自己的应用程序或配置现有应用程序以使用 Keycloak 对用户进行身份验证。
在下一节中,我们将了解如何配置后端应用程序,以根据 Keycloak 颁发的令牌授权访问资源。
创建 Spring Boot 资源服务器
本节中介绍的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/springboot/backend
对于使用 OAuth2 不记名令牌保护的后端应用程序,配置类似于配置前端应用程序。但在这里,应用程序将充当验证 JWT 令牌的资源服务器:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8180/realms/myrealm
让我们通过在项目的根目录中运行以下命令来启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/springboot/backend
$ ./mvnw spring-boot:run
您的应用程序应在 http://localhost:8080 启动并可用。要访问正在运行的应用程序中的资源,您现在需要一个访问令牌。要获取一个,请使用以下命令:
$ export access_token=$(\
curl -X POST http://localhost:8180/realms/myrealm/protocol/openid-connect/token \
-d "client_id=mybackend&client_secret=CLIENT_SECRET" \
-H "content-type: application/x-www-form-urlencoded" \
-d "username=alice&password=alice&grant_type=password" \
| jq --raw-output ".access_token" \
)
您应该将上述命令中对 CLIENT_SECRET 的引用更改为 Keycloak 为 mywebapp 客户端生成的密钥。为此,请导航到 Keycloak 管理控制台中的 mywebapp 客户端详细信息页面,然后单击 Credentials 选项卡。客户端密钥应可从此选项卡的 Client secret (客户端密钥) 字段中获得。
运行此命令后,访问令牌将保存在 access_token 环境变量中,您现在可以访问该应用程序:
$ curl -X GET http://localhost:8080/hello -H "Authorization: Bearer "$access_token
因此,该命令应具有以下输出:
$ Greetings from Spring Boot!
现在,如果您尝试在没有 Bearer Token 的情况下访问应用程序或使用无效的 Bearer Token,您应该会收到 401 状态代码,指示您的请求被禁止:
$ curl -v -X GET http://localhost:8080/hello
在本节中,你了解了如何使用 Spring Security 的 OAuth2/OpenID 库与 Keycloak 集成。我们在这里仅介绍了设置 Spring Security 的主要步骤,以便你可以通过 Keycloak 对用户进行身份验证。有关更多详细信息,请查看 Spring Security 文档,网址为 https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/jwt.html。
在下一节中,我们将了解如何将 Keycloak 与 JavaScript 应用程序集成。
与 JavaScript 应用程序集成
您将找到不同的 JavaScript OpenID Connect 客户端实现,可用于将 Keycloak 与您的 SPA 集成。
在本节中,我们将介绍如何使用 Keycloak JavaScript 适配器,这是 Keycloak 提供的客户端实现,针对在浏览器中运行的基于 JavaScript 的应用程序,以及使用 ReactJS 或 React Native 的应用程序。
本节的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/keycloak-js-adapter
在前面的目录中,您将找到需要遵循和运行即将到来的示例的所有代码。
使用 Keycloak JS 适配器配置应用程序的第一步是添加 keycloak。js 库添加到你的页面中
<script type="text/javascript" src="KC_URL/js/keycloak.js"></script>
这里, KC_URL 是您的 Keycloak 服务器可用的 URL,例如 http://localhost:8180 如果您在本地运行它。
通过从服务器获取库而不是将其嵌入到您的应用程序中,可以保证您始终使用与应用程序正在通信的 Keycloak 服务器兼容的库版本。
现在,该库在您的页面上可用,您需要使用客户端的信息创建一个 keycloak 对象,并在加载浏览器窗口时对其进行初始化:
const keycloak = new Keycloak();
await keycloak.init({ onLoad: "login-required" });
init 方法负责引导适配器。调用此方法时,库将从 public/keycloak.json 文件加载客户端配置,并检查用户是否已通过身份验证。如果他们尚未进行身份验证,适配器会将用户重定向到 Keycloak。用户成功通过身份验证并返回到您的应用程序后,将执行 showProfile 函数,该函数反过来将显示一个包含用户相关信息的页面。
现在,让我们通过运行以下代码来启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/keycloak-js-adapter
$ npm install
$ npm start
您的应用程序应在 http://localhost:8080 启动并可用。尝试访问该 URL 并使用我们在本章开头创建的用户的凭据登录 Keycloak。
如果集成工作正常,您应该被重定向到 Keycloak 进行身份验证。提供用户凭证后,您应该被重定向回应用程序,现在作为经过身份验证的用户。
如果你的应用程序需要使用 bearer token 访问某个后端服务器中的受保护资源,你可以很容易地从 keycloak 对象中获取 access token,并在你发出 HTTP 请求时传递它:
fetch('http://my.api/resource', {
method: 'GET',
headers: {
Authorization: 'Bearer ' + keycloak.token
}
}).then((data) => {console.log(data)})
.catch((error) => {console.log(error)});
Keycloak JavaScript 适配器允许您快速与 Keycloak 集成。此库的构建是由于在创建 OpenID Connect 时缺乏用于 OpenID Connect 的良好 JavaScript 库,由于当今可用的库数量众多,这不再适用。这个适配器在 Keycloak 保护伞下积极维护,并且有很好的文档记录,但仍然专门用于与 Keycloak 集成,而不是一个通用且完全兼容的 OpenID Connect 库。
由于基于浏览器的应用程序的性质,在基于浏览器的应用程序中使用 OpenID Connect 和 OAuth2 会受到安全问题的影响。在选择一个好的库时,您应该遵循 OAuth2 Security Best Practices for Browser-Based Apps(基于浏览器的应用程序的 OAuth2 安全最佳实践)的最佳实践,该最佳实践可在 https://tools.ietf.org/html/draft-ietfoauth-browser-based-apps 上获得。
我们在这里只是触及了皮毛,您可以使用 OpenID 做更多的事情,例如从服务器获取令牌问题、刷新令牌或根据特定时间段自动执行此作以及注销。
有关 Keycloak JavaScript 适配器的更多详细信息,请查看 https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter 中的文档。
在下一节中,我们将了解如何与 Node.js 应用程序集成。
与 Node.js 应用程序集成
对于 Node.js 应用程序,Keycloak 提供了一个称为 Keycloak Node.js 适配器的特定适配器。与其他适配器一样,它的目标是与 Keycloak 集成,而不是通用的 OpenID Connect 客户端实现。
Keycloak Node.js 适配器通过一个简单的 API 对应用程序隐藏了大部分内部结构,您可以使用该 API 来保护应用程序资源。该适配器以 npm 包的形式提供,可以按如下方式安装到您的项目中:
$ npm install keycloak-connect
本节的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/nodejs
在前面的目录中,您将找到一个 frontend 目录和一个 backend 目录,其中包含您需要遵循和运行以下示例的所有代码。
现在,您已经在应用程序中安装了 keycloak-connect 依赖项,我们将了解如何将应用程序配置为客户端和资源服务器。
创建 Node.js 客户端
安装 keycloak-connect 包后,您需要更改应用程序代码,以便它创建一个 keycloak 对象:
var keycloak = new Keycloak({ store: memoryStore });
由于我们正在保护前端应用程序,因此我们希望为用户创建一个本地会话,以便他们在通过身份验证后不会被重定向到 Keycloak。为此,请注意 Keycloak 对象是使用 memoryStore 创建的:
var memoryStore = new session.MemoryStore();
就像其他 Keycloak 适配器一样,配置是从包含 client 配置的 keycloak.json 文件中读取的:
{
"realm": "myrealm",
"auth-server-url": "${env.KC_URL:http://localhost:8180}",
"resource": "mywebapp",
"credentials" : {
"secret" : "CLIENT_SECRET"
}
您应该在 keycloak.json 文件中更改对 CLIENT_SECRET 的引用,其中包含 Keycloak 为 mywebapp 客户端生成的密钥。为此,请导航到 Keycloak 管理控制台中的 mywebapp 客户端详细信息页面,然后单击 Credentials 选项卡。客户端密钥应可从此选项卡的 Client secret (客户端密钥) 字段中获得。
下一步是将适配器安装为中间件,以便您可以使用它来保护应用程序中的资源:
app.use(keycloak.middleware());
现在,中间件已安装完毕,保护应用程序中的资源应该像执行以下作一样简单:
app.get('/', keycloak.protect(), function (req, res) {
res.setHeader('content-type', 'text/plain');
res.send('Welcome!');
})
keycloak.protect 方法会自动将必要的功能添加到您的终端节点,以检查用户是否已经过身份验证,以便他们可以在未通过身份验证时重定向到 Keycloak。鉴权成功后,中间件会自动处理来自 Keycloak 的响应,并根据服务端颁发的 Token 为用户建立本地会话。现在,让我们启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/nodejs/frontend
$ npm install
$ npm start
您的应用程序应在 http://localhost:8080 启动并可用。尝试访问该 URL 并使用我们在本章开头创建的用户的凭据登录到 Keycloak。
如果集成工作正常,您应该被重定向到 Keycloak 进行身份验证。提供用户凭证后,您应该被重定向回应用程序,现在作为经过身份验证的用户。
创建 Node.js 资源服务器
此服务器中提供的代码示例可从以下 GitHub 存储库获得:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/nodejs/backen
对于后端应用程序,您可以按如下方式创建 keycloak 对象:
var keycloak = new Keycloak({});
与前端应用程序相比,我们不需要跟踪用户会话;相反,我们必须依靠 Bearer Token 来授权请求。
与前面的示例类似,我们还需要使用 client 配置更新 keycloak.json 文件:
{
"realm": "myrealm",
"bearer-only": true,
"auth-server-url": "${env.KC_URL:http://localhost:8180}",
"resource": "mybackend"
}
在此配置中,我们将此应用程序显式标记为仅持有者,以便它只接受持有者令牌,从而强制适配器通过对令牌执行本地验证和内省来检查请求是否可以访问应用程序中的资源。
下一步是将适配器安装为中间件,以便您可以使用它来保护应用程序中的资源:
app.use(keycloak.middleware());
现在,中间件已安装完毕,保护应用程序中的资源应该像执行以下作一样简单:
app.get('/hello', keycloak.protect(), function (req, res) {
res.setHeader('content-type', 'text/plain');
res.send('Access granted to protected resource');
});
keycloak.protect 方法会自动将bearer token 授权添加到您的终端节点,以便包含具有有效令牌的授权标头的请求可以获取应用程序中的受保护资源。
现在,让我们启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/nodejs/backend
$ npm install
$ npm start
您的应用程序应在 http://localhost:8080 启动并可用。要访问正在运行的应用程序中的资源,您需要一个访问令牌。要获取一个,请使用以下命令:
$ export access_token=$(\
curl -X POST http://localhost:8180/realms/myrealm/protocol/openid-connect/token \
-d "client_id=mybackend&client_secret=CLIENT_SECRET" \
-H "content-type: application/x-www-form-urlencoded" \
-d "username=alice&password=alice&grant_type=password" \
| jq --raw-output ".access_token" \
)
您应该使用 Keycloak 为 mybackend 客户端生成的密钥更改上述命令中对 CLIENT_SECRET 的引用。为此,请导航到 Keycloak 中的 mybackend 客户端详细信息页面,然后单击 Credentials 选项卡。客户端密钥应可从此选项卡的 Client secret (客户端密钥) 字段中获得。
运行该命令后,访问令牌将保存在 access_token 环境变量中,这意味着您现在可以访问该应用程序:
$ curl -v -X GET http://localhost:8080/hello -H "Authorization: Bearer $access_token"
因此,您应该会收到以下输出:
$ Access granted to protected resource
现在,如果您尝试在没有 bearer token 的情况下访问应用程序或使用无效的 bearer token,您应该会收到 403 状态代码,表明您的请求被禁止:
$ curl -v -X GET http://localhost:8080/hello
在配置和使用方面,您可以使用 Keycloak Node.js 适配器做更多的事情。您应该能够使用 keycloak.protect 执行基于角色的访问控制并获取代表经过身份验证的使用者的令牌。
有关 Keycloak Node.js 适配器的更多详细信息,请查看https://www.keycloak.org/docs/latest/securing_apps/#_nodejs_adapter
在本节中,您学习了如何配置 Node.js 应用程序,以便您可以使用 keycloak-connect 库与 Keycloak 集成。接下来,您将学习利用 proxied architectural style,如果到目前为止提供的任何选项都不足以满足您的要求,这将非常有用。
使用反向代理
通过在你的应用程序前运行 Keycloak,你可以使用反向代理为你的应用程序添加额外的功能。最常见的代理为 OpenID Connect 提供支持,在这种情况下,启用身份验证只需更改代理配置。
使用代理是否比在应用程序中集成代码和配置更好,实际上取决于具体的用例。在某些情况下,它可能是你唯一的选择,或者是能为你节省宝贵时间的选择,让你无需实现自己的集成代码,即使你的应用程序所使用的技术栈有可用的库。如今,对 OpenID Connect 和 OAuth2 的支持是代理的一项必备功能,并且在大多数代理中你都能找到对这些协议的支持,无论它们是开源的还是专有的。例如,两个最流行的代理 ——Apache HTTP 服务器和 Nginx,为这些协议提供了必要的扩展。
在本节中,我们将介绍如何在我们的应用程序前面设置 Apache HTTP Server,以便我们可以将其与 Keycloak 集成并使用 mod_auth_oidc 对用户进行身份验证。有关如何安装它的文档可在 https://github.com/zmartzone/mod_auth_openidc 上找到。
安装 Apache HTTP Server 和模块后,我们需要配置服务器,以便我们可以代理我们的应用程序并使用该模块来确保用户通过 Keycloak 进行身份验证:
LoadModule auth_openidc_module modules/mod_auth_openidc.so
ServerName localhost
<VirtualHost *:80>
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
OIDCCryptoPassphrase CHANGE_ME
OIDCProviderMetadataURL http://localhost:8180/realms/myrealm/.well-known/openid-configuration
OIDCClientID proxy-client
OIDCClientSecret CLIENT_SECRET
OIDCRedirectURI http://localhost/callback
OIDCCookieDomain localhost
OIDCCookiePath /
OIDCCookieSameSite On
<Location />
AuthType openid-connect
Require valid-user
</Location>
</VirtualHost>
您应该使用 Keycloak 为 mywebapp 客户端生成的密钥更改上述配置中对 CLIENT_SECRET 的引用。为此,导航到 Keycloak 中的 mywebapp 客户端详细信息页面,然后单击 Credentials 选项卡。客户端密钥应可从此选项卡的 Client secret (客户端密钥) 字段中获得。
现在,让我们启动应用程序:
$ cd Keycloak---Identity-and-Access-Management-for-Modern-Applications-2nd-Edition/ch7/reverse-proxy/app/
$ npm install
$ npm start
您的应用程序应在 http://localhost 启动并可用。尝试访问该 URL 并使用我们在本章开头创建的用户的凭据登录到 Keycloak。
如果集成工作正常,您应该被重定向到 Keycloak 进行身份验证。提供用户凭证后,您应该被重定向回应用程序,现在作为经过身份验证的用户。
尽量不要实现自己的集成
OAuth2 和 OpenID Connect 是简单的协议,它们的简单性部分是由于我们努力使协议更易于客户端应用程序使用,但不一定是从头开始实现它们。你可能会想编写自己的代码来与 Keycloak 集成,但这通常是一个糟糕的选择。
您应该依赖部署应用程序的平台提供的众所周知且广泛使用的库、框架或功能。
通过这样做,您可以专注于您的业务,最重要的是,委托给专门关注这些标准的人员,以使其实施始终与最新版本的规范以及安全漏洞和安全最佳实践的任何修复程序保持同步。
此外,请记住,使用实现的人越多,您遇到错误和安全漏洞的可能性就越小,这不仅会影响您的应用程序,还会影响您的组织。
总结
在本章中,您学习了如何将 Keycloak 与不同类型的应用程序集成,具体取决于它们使用的技术堆栈以及它们运行的平台。您还了解了使用众所周知的成熟开放标准的重要性,以及这在互作性方面意味着什么。这意味着您可以自由选择最能满足您需求的 OpenID Connect 客户端实施,同时仍然遵守合规性,并使您的应用程序保持与 OAuth2 和 OpenID Connect 最佳实践和安全修复程序保持同步。
最后,您了解了为什么应该避免实施自己的集成,以及如果其他选项都不适合您,则在寻找替代方案时应考虑的事项。
在下一章中,您将了解可用于保护应用程序资源的不同授权策略。
问题
-
与 Keycloak 集成的最佳方式是什么?
-
如果 Keycloak 适配器适合我的技术堆栈,我是否应该始终考虑使用它们?
-
您应该如何使用 Keycloak 保护本机或移动应用程序?
-
云原生应用程序的最佳集成选项是什么?
延伸阅读
有关本章所涵盖主题的更多信息,请参阅以下链接:
- 经认证的 OpenID Connect 实施:https://openid.net/developers/certified
- 适用于基于浏览器的应用程序的 OAuth 2.0:https://tools.ietf.org/html/draft-ietf-oauthbrowser-based-apps-07
- OAuth 2.0 安全当前最佳实践:https://tools.ietf.org/html/draft-ietfoauth-security-topics-16
- Keycloak 快速入门:https://github.com/keycloak/keycloak-quickstarts
- 保护应用程序和服务指南:https://www.keycloak.org/docs/latest/securing_apps