2.5. 红帽构建的 Keycloak Node.js 适配器
Red Hat build of Keycloak 提供了一个在 Connect 上构建的 Node.js 适配器,以保护服务器端 JavaScript 应用 - 目标足够灵活,以便与 Express.js 等框架集成。
要使用 Node.js 适配器,您必须首先在红帽构建的 Keycloak 管理控制台中为应用程序创建一个客户端。适配器支持 public, confidential, 和 bearer-only 访问类型。哪个选项取决于用例场景。
创建客户端后,点右上角的 Action,然后选择 Download adapter config。对于 Format,请选择 *Keycloak OIDC JSON 并点 Download。下载的 keycloak.json
文件位于项目的根目录中。
2.5.1. 安装 复制链接链接已复制到粘贴板!
假设您已经安装了 Node.js,请为您的应用程序创建一个文件夹:
mkdir myapp && cd myapp
mkdir myapp && cd myapp
使用 npm init
命令为应用程序创建 package.json
。现在,在依赖项列表中添加红帽构建的 Keycloak 连接适配器:
"dependencies": { "keycloak-connect": "file:keycloak-connect-24.0.9.tgz" }
"dependencies": {
"keycloak-connect": "file:keycloak-connect-24.0.9.tgz"
}
2.5.2. 使用方法 复制链接链接已复制到粘贴板!
- 实例化 Keycloak 类
-
Keycloak
类为配置和与应用程序集成提供了一个中央点。最简单的创建涉及任何参数。
在项目的根目录中,创建一个名为 server.js
的文件并添加以下代码:
const session = require('express-session'); const Keycloak = require('keycloak-connect'); const memoryStore = new session.MemoryStore(); const keycloak = new Keycloak({ store: memoryStore });
const session = require('express-session');
const Keycloak = require('keycloak-connect');
const memoryStore = new session.MemoryStore();
const keycloak = new Keycloak({ store: memoryStore });
安装 express-session
依赖项:
npm install express-session
npm install express-session
要启动 server.js
脚本,请在 package.json
的"scripts"部分中添加以下内容:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" },
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
现在,我们可以通过以下命令运行服务器:
npm run start
npm run start
默认情况下,这将找到一个名为 keycloak.json
的文件,以及应用程序的主可执行文件(在根文件夹上),以初始化红帽构建的 Keycloak 特定设置,如公钥、域名、各种 URL。
在这种情况下,红帽构建的 Keycloak 部署需要访问红帽构建的 Keycloak 管理控制台。
请访问有关如何使用 Podman 或 Docker部署红帽构建的 Keycloak 管理控制台的链接
现在,我们已准备好通过访问 Red Hat build of Keycloak Admin Console 。
将下载的文件粘贴到项目的根目录上。
使用此方法实例化会导致使用所有合理的默认值。另外,也可以提供配置对象,而不是 keycloak.json
文件:
应用程序也可以使用以下方法将用户重定向到首选身份提供程序:
const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
- 配置 Web 会话存储
-
如果要使用 Web 会话管理服务器端状态进行身份验证,则需要使用至少
存储
参数初始化Keycloak (…)
,从而传递express-session
使用的实际会话存储。
- 传递自定义范围值
-
默认情况下,范围值
openid
作为查询参数传递给红帽构建的 Keycloak 登录 URL,但您可以添加额外的自定义值:
const keycloak = new Keycloak({ scope: 'offline_access' });
const keycloak = new Keycloak({ scope: 'offline_access' });
2.5.3. 安装中间件 复制链接链接已复制到粘贴板!
实例化后,将中间件安装到支持连接的应用程序中:
要做到这一点,必须首先安装 Express:
npm install express
npm install express
然后,需要我们的项目中的 Express,如下所示:
const express = require('express'); const app = express();
const express = require('express');
const app = express();
通过在以下代码中添加,并在 Express 中配置 Keycloak 中间件:
app.use( keycloak.middleware() );
app.use( keycloak.middleware() );
最后,我们通过将以下代码添加到 main.js
,将服务器设置为在端口 3000 上侦听 HTTP 请求:
app.listen(3000, function () { console.log('App listening on port 3000'); });
app.listen(3000, function () {
console.log('App listening on port 3000');
});
2.5.4. 配置代理配置 复制链接链接已复制到粘贴板!
如果应用程序在终止 SSL connection Express 的代理后面运行,则必须根据 代理后面的表达 配置 SSL connection Express。使用不正确的代理配置可能会导致生成无效的重定向 URI。
配置示例:
const app = express(); app.set( 'trust proxy', true ); app.use( keycloak.middleware() );
const app = express();
app.set( 'trust proxy', true );
app.use( keycloak.middleware() );
2.5.5. 保护资源 复制链接链接已复制到粘贴板!
- 简单身份验证
-
要强制用户在访问资源前必须经过身份验证,只需使用
keycloak.protect ()
的 no-argument 版本:
app.get( '/complain', keycloak.protect(), complaintHandler );
app.get( '/complain', keycloak.protect(), complaintHandler );
- 基于角色的授权
- 使用当前应用程序的应用程序角色来保护资源:
app.get( '/special', keycloak.protect('special'), specialHandler );
app.get( '/special', keycloak.protect('special'), specialHandler );
使用不同应用程序的应用程序角色来 保护资源 :
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
使用 realm 角色保护资源:
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
- 基于资源的授权
-
基于资源的授权允许您根据 Keycloak 中定义的一组策略来保护资源及其特定方法/操作,从而从应用程序外部授权。这可以通过公开
keycloak.enforcer
方法来实现,您可以使用该方法来保护 resources metric
app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);
app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);
keycloak-enforcer
方法以两种模式运行,具体取决于 response_mode
配置选项的值。
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);
如果将 response_mode
设置为 令牌
,则会代表发送到应用程序的 bearer 令牌代表从服务器获取权限。在这种情况下,Keycloak 会发布一个新的访问令牌,其权限由服务器授予。如果服务器没有响应具有预期权限的令牌,则请求将被拒绝。使用此模式时,您应能够从请求获取令牌,如下所示:
当应用程序使用会话且您想要缓存来自服务器的之前决策时,首选此模式,并自动处理刷新令牌。此模式对于充当客户端和资源服务器的应用程序特别有用。
如果 response_mode
设为 permissions
(默认模式),服务器仅返回已授予权限的列表,而不发出新的访问令牌。除了没有发布新令牌外,此方法还会公开服务器通过 请求
授予的权限,如下所示:
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) { const permissions = req.permissions; // show user profile });
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) {
const permissions = req.permissions;
// show user profile
});
无论使用中的 response_mode
是什么,keycloak.enforcer
方法都会首先尝试检查发送到应用程序的 bearer 令牌中的权限。如果 bearer 令牌已经执行了预期的权限,则不需要与服务器交互来获取决策。当您的客户端能够在访问受保护的资源前从具有预期权限的服务器获取访问令牌时,这特别有用,因此他们可以使用 Keycloak 授权服务(如增量授权服务)提供的一些功能,并避免对服务器的额外的请求。
默认情况下,策略实施器将使用定义为应用程序的 client_id
(例如,通过 keycloak.json
)来引用支持 Keycloak Authorization Services 的 Keycloak 中的客户端。在这种情况下,客户端不能是公共的,它实际上是一个资源服务器。
如果您的应用程序同时充当公共客户端(frontend)和资源服务器(backend),您可以使用以下配置在 Keycloak 中引用您要强制执行的策略的不同客户端:
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
建议您在 Keycloak 中使用不同的客户端来代表您的前端和后端。
如果您使用 Keycloak 授权服务启用保护的应用程序,并在 keycloak.json
中定义客户端凭证,您可以将其他声明推送到服务器,并使其可用于您的策略以做出决策。为此,您可以定义一个 claims
配置选项,它需要一个会返回一个带有您要推送的声明的 JSON 的函数
:
有关如何将 Keycloak 配置为保护您的应用程序资源的更多详细信息,请参阅 授权服务指南。
- 高级授权
- 要根据 URL 本身的部分来保护资源,假设每个部分都有一个角色:
function protectBySection(token, request) { return token.hasRole( request.params.section ); } app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
function protectBySection(token, request) {
return token.hasRole( request.params.section );
}
app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
高级登录配置:
默认情况下,除非您的客户端为 bearer-only,否则所有未授权的请求都会被重定向到红帽构建的 Keycloak 登录页面。但是,机密或公共客户端可以同时托管可浏览和 API 端点。要防止对未经身份验证的 API 请求进行重定向,并返回 HTTP 401,您可以覆盖 redirectToLogin 功能。
例如,此覆盖检查 URL 是否包含 /api/ 并禁用登录重定向:
Keycloak.prototype.redirectToLogin = function(req) { const apiReqMatcher = /\/api\//i; return !apiReqMatcher.test(req.originalUrl || req.url); };
Keycloak.prototype.redirectToLogin = function(req) {
const apiReqMatcher = /\/api\//i;
return !apiReqMatcher.test(req.originalUrl || req.url);
};
2.5.6. 其他 URL 复制链接链接已复制到粘贴板!
- 显式用户触发的退出
-
默认情况下,中间件会捕获对
/logout
的调用,以便通过以 Keycloak 为中心的注销工作流向用户发送。这可以通过在middleware()
调用中指定logout
配置参数来更改:
app.use( keycloak.middleware( { logout: '/logoff' } ));
app.use( keycloak.middleware( { logout: '/logoff' } ));
当调用 user-triggered logout 时,可以传递查询参数 redirect_url
:
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
然后,此参数被用作 OIDC 注销端点的重定向 URL,用户将重定向到 https://example.com/logged/out
。
- 红帽构建的 Keycloak 管理回调
-
另外,中间件还支持来自红帽构建的 Keycloak 控制台的回调,以注销单个会话或所有会话。默认情况下,这些类型的管理回调相对于
/
的根 URL,但可以通过向middleware ()
调用提供admin
参数来更改:
app.use( keycloak.middleware( { admin: '/callbacks' } );
app.use( keycloak.middleware( { admin: '/callbacks' } );
2.5.7. 完成示例 复制链接链接已复制到粘贴板!
使用 Node.js 适配器使用的完整示例可在 Node.js 的 Keycloak 快速入门中找到