2.5. 红帽构建的 Keycloak Node.js 适配器
Red Hat build of Keycloak 提供了一个在 Connect 上构建的 Node.js 适配器,以保护服务器端 JavaScript 应用 - 目标足够灵活,以便与 Express.js 等框架集成。
要使用 Node.js 适配器,您必须首先在红帽构建的 Keycloak 管理控制台中为应用程序创建一个客户端。适配器支持 public、Conf 和 bearer-only 访问类型。哪个选择取决于用例。
创建客户端后,点 Installation 选项卡,为 Format Option 选择 Red Hat build of 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-22.0.13+redhat-00001.tgz"
}
"dependencies": {
"keycloak-connect": "file:keycloak-connect-22.0.13+redhat-00001.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 会话来管理服务器端状态进行身份验证,则需要至少使用
store参数初始化Keycloak (…),传递表达会话使用的实际会话存储。
- 传递自定义范围值
-
默认情况下,范围值
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 的代理后面运行,则必须根据 代理指南后面的表达 进行配置。使用不正确的代理配置可能会导致生成无效的重定向 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. 保护资源 复制链接链接已复制到粘贴板!
- 简单身份验证
-
要强制用户在访问资源前验证用户,只需使用 no-argument 版本
keycloak.protect ():
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 中定义的一组策略来保护资源及其特定方法/actions,从而从外部化应用程序中的授权。这可以通过公开一个
keycloak.enforcer方法来实现,您可以使用它来保护资源 192.168.1.0/24
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 设为 权限 (默认模式),服务器只返回授予的权限列表,而不发出新的访问令牌。除了不发布新令牌外,此方法还通过 请求 公开服务器授予的权限,如下所示:
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 授权服务提供的一些功能,如增量授权,并在 keycloak.enforcer 强制访问资源时从服务器获取访问令牌。
默认情况下,策略 enforcer 将使用定义为应用程序(例如,通过 keycloak.json)定义的 client_id 来引用支持 Keycloak 授权服务的 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' } ));
当用户触发的注销被调用查询参数 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 控制台的回调,以注销单个会话或所有会话。默认情况下,这些类型的 admin 回调相对于
/的 root URL,但可以通过向middleware ()调用提供admin参数来更改:
app.use( keycloak.middleware( { admin: '/callbacks' } );
app.use( keycloak.middleware( { admin: '/callbacks' } );
2.5.7. 完成示例 复制链接链接已复制到粘贴板!
使用 Node.js 适配器使用的完整示例可在 Node.js 的 Keycloak 快速入门中找到