2.3. Node.js adapter
Red Hat Single Sign-On 提供了一个在 Connect 上创建的 Node.js 适配器来保护服务器端 JavaScript 应用 - 目标足以与 Express.js 等框架集成。
要使用 Node.js 适配器,首先必须在 Red Hat Single Sign-On Admin Console 中为您的应用程序创建客户端。适配器支持公共、机密和只读访问类型。哪一种选择取决于用例方案。
创建客户端后,点 Installation
选项卡,选择 Red Hat Single Sign-On OIDC JSON
for Format option
,然后点 Download
。下载的 keycloak.json
文件应当位于项目的根目录下。
2.3.1. 安装
假设您已经安装了 Node.js,并为您的应用程序创建一个文件夹:
mkdir myapp && cd myapp
使用 npm init
命令为您的应用程序创建 package.json
。现在,在依赖列表中添加 Red Hat Single Sign-On 连接适配器:
"dependencies": { "keycloak-connect": "file:keycloak-connect-18.0.7.tgz" }
2.3.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 });
安装 express-session
依赖项:
npm install express-session
要启动 server.js
脚本,请在 package.json
的"scripts"部分中添加以下命令:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start" "node server.js" },
现在,我们可以使用以下命令运行我们的服务器:
npm run start
默认情况下,这将查找名为 keycloak.json
的文件,以及应用程序的主要可执行文件,本例中为 root 文件夹,以初始化特定于密钥的设置,如公钥、域名和各种 URL。
在这种情况下,需要 Keycloak 部署来访问 Keycloak admin 控制台。
请访问链接如何使用 Podman 或 Docker部署 Keycloak 管理控制台
现在,我们已准备好通过访问 Red Hat Single Sign-On Admin Console keycloak.json
文件
将下载的 文件粘贴到我们项目的根目录中。
这个方法的实例化会导致使用所有合理的默认值。另外,也可以提供配置对象,而不是 keycloak.json
文件:
const kcConfig = { clientId: 'myclient', bearerOnly: true, serverUrl: 'http://localhost:8080/auth', realm: 'myrealm', realmPublicKey: 'MIIBIjANB...' }; const keycloak = new Keycloak({ store: memoryStore }, kcConfig);
应用程序还可以使用以下方法将用户重定向到首选身份提供程序:
const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
- 配置 web 会话存储
-
如果要使用 web 会话来管理服务器端身份验证的状态,则需要使用至少一个
store
参数初始化Keycloak(…)
,并传递express-session
使用的实际会话存储。
const session = require('express-session'); const memoryStore = new session.MemoryStore(); // Configure session app.use( session({ secret: 'mySecret', resave: false, saveUninitialized: true, store: memoryStore, }) ); const keycloak = new Keycloak({ store: memoryStore });
- 传递自定义范围值
-
默认情况下,范围值
openid
作为参数传递给 Red Hat Single Sign-On 的登录 URL,但您可以添加额外的自定义值:
const keycloak = new Keycloak({ scope: 'offline_access' });
2.3.3. 安装中间件
实例化后,将中间件安装到连接功能的应用程序中:
为此,首先需要安装 Express:
npm install express
然后,我们的项目中需要 Express,如下所示:
const express = require('express'); const app = express();
并在以下代码中添加一个 Keycloak 中间件,并在 Express 中配置 Keycloak 中间件:
app.use( keycloak.middleware() );
最后,我们把服务器设置为在端口 3000 上侦听 HTTP 请求,方法是将以下代码添加到 main.js
中:
app.listen(3000, function () { console.log('App listening on port 3000'); });
2.3.4. 配置代理
如果应用程序在 代理的代理后面运行,则必须根据代理指南中的表达 来配置终止 SSL 连接 Express。使用不正确的代理配置可能会导致生成的重定向 URI。
配置示例:
const app = express(); app.set( 'trust proxy', true ); app.use( keycloak.middleware() );
2.3.5. 检查身份验证
要在访问资源之前检查用户进行身份验证,只需使用 keycloak.checkSso()
。只有在用户已经登录后,它将才进行身份验证。如果用户没有登录,浏览器将重新重定向到原先请求的 URL,并保持未经身份验证的:
app.get( '/check-sso', keycloak.checkSso(), checkSsoHandler );
2.3.6. 保护资源
- 简单验证
-
要强制在访问资源前必须验证用户,只需使用
keycloak.protect()
的 no-argument 版本:
app.get( '/complain', keycloak.protect(), complaintHandler );
- 基于角色的授权
- 使用当前应用程序的应用程序角色保护资源:
app.get( '/special', keycloak.protect('special'), specialHandler );
使用不同应用程序的应用程序角色 保护资源 :
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
使用 realm 角色保护资源:
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
- 基于资源的授权
-
借助基于资源的身份验证,您可以保护资源及其具体方法/操作,** 基于 Keycloak 中定义的一组策略,从而可以从应用程序外部化授权。这可以通过公开可用于保护资源的
keycloak.enforcer
方法来实现。*
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);
如果将 response_mode
设为 token
,则代表您的应用程序的 bearer 令牌代表的主体从服务器获得权限。在本例中,由由服务器授予权限的 Keycloak 发布新访问令牌。如果服务器没有响应具有预期权限的令牌,则请求被拒绝。使用此模式时,您应该能够从请求中获取令牌,如下所示:
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) { const token = req.kauth.grant.access_token.content; const permissions = token.authorization ? token.authorization.permissions : undefined; // show user profile });
当应用程序使用会话时,首选使用这个模式,并希望从服务器缓存以前的决定,并自动处理刷新令牌。对于作为客户端和服务器的应用程序,此模式特别有用。
如果将 response_mode
设为 permissions
(默认模式),服务器只返回授予权限的列表,而不会发出新的访问令牌。除了不提供新令牌外,此方法还通过 请求
公开服务器授予的权限,如下所示:
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) { const permissions = req.permissions; // show user profile });
无论使用是否使用了 response_mode
,cloak .enforcer
方法将首先尝试检查发送到应用程序的 bearer 令牌中的权限。如果 bearer 令牌已执行预期的权限,则不需要与服务器交互来获取决策。当您的客户端能够在访问受保护的资源前从具有预期权限的服务器获取访问令牌时,可以使用 Keycloak Authorization Services(如增量授权)提供的一些功能,并避免在 keycloak.enforcer
被强制访问资源时对服务器获取访问令牌。
默认情况下,策略 enforcer 将使用定义为应用程序的 client_id
(例如,通过 keycloak.json
)引用支持 Keycloak 授权服务的 Keycloak 中的客户端。在这种情况下,如果客户端实际上是资源服务器,客户端不能是公共的。
如果您的应用程序同时充当公共客户端(frontend)和资源服务器,您可以使用以下配置使用您要强制执行的策略引用 Keycloak 中的不同客户端:
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
建议您在 Keycloak 中使用不同的客户端来代表您的前端和后端。
如果使用 Keycloak 授权服务启用了保护应用程序,且您在 keycloak.json
中定义了客户端凭证,您可以将额外声明推送到服务器,并将其提供给您的策略以做出决策。为此,您可以定义一个 claims
配置选项,它需要一个会返回一个带有您要推送的声明的 JSON 的函数
:
app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], { claims: function(request) { return { "http.uri": ["/protected/resource"], "user.agent": // get user agent from request } } }), function (req, res) { // access granted
有关如何配置 Keycloak 以保护应用程序资源的详情,请查看授权服务 指南。
- 高级授权
- 要保护基于 URL 本身的部分资源,假设每个部分都存在角色:
function protectBySection(token, request) { return token.hasRole( request.params.section ); } app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
高级登录配置:
默认情况下,所有未授权的请求都将被重定向到红帽单点登录登录页面,除非您的客户端只有 bearer-only。但是,机密或公共客户端可以托管可浏览和 API 端点。要防止对未经身份验证的 API 请求进行重定向并返回 HTTP 401,您可以覆盖 redirectToLogin 功能。
例如,这个覆盖会检查 URL 包含 /api/ 并禁用登录重定向:
Keycloak.prototype.redirectToLogin = function(req) { const apiReqMatcher = /\/api\//i; return !apiReqMatcher.test(req.originalUrl || req.url); };
2.3.7. 其他 URL
- 显式用户触发注销
-
默认情况下,中间件捕获对
/logout
的调用,以通过 Red Hat Single Sign-On-centric logout 工作流向用户发送。这可以通过在middleware()
调用中指定logout
配置参数来更改:
app.use( keycloak.middleware( { logout: '/logoff' } ));
当调用查询参数 redirect_url
时,可以传递用户触发的 logout:
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
然后,这个参数被用作 OIDC logout 端点的重定向 url,并将用户重定向到 https://example.com/logged/out
。
- Red Hat Single Sign-On Admin Callbacks
-
另外,中间件支持来自红帽单点登录控制台的回调,以注销单个会话或所有会话。默认情况下,这些管理员回调与
/
根 URL 相对进行的,但可通过向 Middleware()
调用提供admin
参数来更改:
app.use( keycloak.middleware( { admin: '/callbacks' } );
2.3.8. 完整示例
使用 Node.js 适配器用法的完整示例可在 Node.js 的 KeycloakQuickstart 中找到