使用 Auth0 和 Cube.js 进行多租户分析 🔐 — 完整指南
安全……何必呢?🤔
步骤 0. 开放可访问的分析应用程序
步骤 1. 使用 JWT 进行身份验证
步骤 2. 使用 JWT 授权
步骤3.通过Auth0进行身份验证
步骤 4. 使用审计日志进行问责
简而言之:在本指南中,我们将学习如何使用符合行业标准且经过验证的身份验证机制(例如 JSON Web Tokens、JSON Web Keys 和 OAuth 2.0 协议)来保护 Web 应用程序的安全。我们将从一个可公开访问的、不安全的分析应用程序开始,逐步将其转变为一个安全的多租户应用程序,并采用基于角色的访问控制和外部身份验证提供程序。我们将使用Cube.js构建分析应用程序,并使用 Auth0 对用户进行身份验证。
安全……何必呢?🤔
这个问题问得好!正如著名安全专家乔治·奥威尔所说:“所有用户都是平等的,但有些用户比其他用户更平等。”
通常,保护应用程序安全的需求根植于一个前提,即某些用户应该被允许做比其他用户更多的事情:访问应用程序、读取或更新数据、邀请其他用户等。为了满足这一需求,应用程序应该实现IAAA,即它应该能够执行:
- 身份识别。询问用户“你是谁?”
- 身份验证。检查用户是否确实是他们声称的身份
- 授权。允许用户根据其身份执行某些操作
- 问责制。记录用户的操作以供将来审查
在本指南中,我们将通过一系列简单易懂的步骤来保护 Web 应用程序、实施 IAAA 和用户行业标准机制:
- 步骤 0.使用 Cube.js 启动一个开放可访问的分析应用程序
- 步骤 1.使用签名和加密的 JSON Web 令牌添加身份验证
- 步骤 2.使用存储在 JSON Web 令牌中的安全声明添加授权、多租户和基于角色的访问控制
- 步骤 3.通过外部提供商使用 Auth0添加身份识别,并使用 JSON Web Keys 验证 JSON Web Tokens
- 步骤 4.使用审计日志添加责任
- 第五步:构建安全的应用,感觉棒极了😎
另外,这里有一个您可以立即试用的现场演示。它的外观和使用体验与我们将要构建的应用程序完全一样,也就是说,它允许您使用 Auth0 进行身份验证并查询分析 API。正如您所料,源代码位于GitHub上。
好吧,让我们开始吧——别忘了戴口罩!🤿
步骤 0. 开放可访问的分析应用程序
为了确保 Web 应用程序的安全,我们需要一个这样的框架。因此,我们将使用Cube.js创建一个分析 API,以及一个与 API 交互并允许用户访问数据库中存储的电商数据的前端应用。
Cube.js是一个开源的分析 API 平台,它允许你在任何数据库上创建 API,并提供工具来探索数据、帮助构建数据可视化以及调整性能。让我们看看它是如何工作的。
第一步是创建一个新的 Cube.js 项目。我假设你已经在你的机器上安装了Node.js。注意,你也可以将 Docker与 Cube.js 一起使用。在你的控制台中运行:
npx cubejs-cli create multi-tenant-analytics -d postgres
现在,你的新 Cube.js 项目已在multi-tenant-analytics
包含几个文件的文件夹中。让我们导航到此文件夹。
第二步是将数据库凭据添加到.env
文件。Cube.js将从此文件中获取其配置选项。让我们将托管在云端 Postgres 数据库中的演示电商数据集的凭据添加到文件中。请确保您的.env
文件如下所示,或者指定您自己的凭据:
# Cube.js environment variables: https://cube.dev/docs/reference/environment-variables
CUBEJS_DB_TYPE=postgres
CUBEJS_DB_HOST=demo-db.cube.dev
CUBEJS_DB_PORT=5432
CUBEJS_DB_SSL=true
CUBEJS_DB_USER=cube
CUBEJS_DB_PASS=12345
CUBEJS_DB_NAME=ecom
CUBEJS_DEV_MODE=true
CUBEJS_WEB_SOCKETS=false
CUBEJS_API_SECRET=SECRET
第三步是启动 Cube.js API。在控制台中运行:
npm run dev
好了,我们的分析 API 已经准备好了!你应该在控制台中看到以下内容:
请注意,它显示该 API 目前处于开发模式,因此身份验证检查已禁用。这意味着任何人都可以访问它。我们很快就会修复这个问题。
第四步是检查身份验证是否已禁用。在浏览器中打开http://localhost:4000
并访问开发者游乐场。它是 Cube.js 的一部分,可用于探索数据、使用模板创建前端应用程序等。
请前往“Schema”选项卡,勾选public
侧边栏中的“表格”,然后点击Generate Schema
。Cube.js 将生成一个数据模式,它是数据库中数据的高级描述。它允许您向 API 发送特定于域的请求,而无需编写冗长的 SQL 查询。
假设我们知道数据集中的电商订单可能处于不同的状态(处理中、已发货等),并且我们想知道每种状态的订单数量。您可以在“构建”选项卡上选择这些度量和维度,并立即查看结果。选择Orders.count
度量和维度后的结果如下:Orders.status
它之所以有效,是因为 Developer Playground 向 API 发送了请求。因此,你可以在控制台中运行以下命令来获得相同的结果:
curl http://localhost:4000/cubejs-api/v1/load \
-G -s --data-urlencode 'query={"measures": ["Orders.count"], "dimensions": ["Orders.status"]}' \
| jq '.data'
请注意,它使用了jq
命令行JSON 处理器实用程序来美化输出。您可以安装 jq
或删除命令中的最后一行。无论如何,您将获得熟悉的结果:
‼️我们无需任何身份验证即可检索数据。API没有发送任何安全标头,但它却返回了结果。因此,我们创建了一个开放访问的分析 API。
最后一步是创建前端应用。请返回开发者平台http://localhost:4000
,前往“仪表盘应用”选项卡,选择“创建您自己的应用”,然后点击“确定”,接受默认设置。
只需几秒钟,文件夹中就会出现一个新创建的前端应用dashboard-app
。点击“启动仪表板应用”即可运行它,或者直接进入dashboard-app
文件夹并在控制台中运行:
npm run start
你会看到这样的前端应用程序:
如果您转到“探索”选项卡,再次选择Orders Count
度量和维度,您将看到:Orders Status
这意味着我们已经成功创建了一个可以向不安全 API 发出请求的前端应用。您也可以点击“添加到仪表盘”按钮,将此查询保留在“仪表盘”选项卡上。
现在,当我们正在航行于一些危险的水域时,是时候进行下一步并添加身份验证了🤿
步骤 1. 使用 JWT 进行身份验证
众所周知,身份验证的本质是确保我们的应用程序只由经过验证的用户访问,而不是其他任何人。我们该如何实现这一点?
我们可以要求用户从 Web 应用向 API 传递一条信息。如果我们能够验证这条信息有效并通过检查,我们就会允许该用户访问我们的应用。这样的信息通常称为令牌。
JSON Web Tokens是一种开放的、行业标准的方法,用于用附加信息(所谓的声明)来表示这些信息。Cube.js 与许多其他应用程序一样,使用 JWT 来验证对 API 的请求。
现在,我们将更新 API 来验证请求并确保 Web 应用程序发送正确的 JWT。
首先,让我们更新 Cube.js 配置。在.env
文件中,您可以找到以下选项:
CUBEJS_DEV_MODE=true
CUBEJS_API_SECRET=SECRET
第一个选项控制 Cube.js 是否应在开发模式下运行。在该模式下,所有身份验证检查均被禁用。第二个选项设置用于对 JWT 进行加密签名的密钥。这意味着,如果我们将此密钥保密,则只有我们才能为用户生成 JWT。
让我们更新这些选项(并添加一个新选项,如文档中所述):
CUBEJS_DEV_MODE=false
CUBEJS_API_SECRET=NEW_SECRET
CUBEJS_CACHE_AND_QUEUE_DRIVER=memory
与其直接使用NEW_SECRET
,不如生成并使用一个新的伪随机字符串。一种方法是使用在线生成器。另一种选择是在控制台中运行这个简单的 Python 命令,然后复制粘贴结果:
python -c 'import sys,uuid; sys.stdout.write(uuid.uuid4().hex)'
之后,保存更新的.env
文件,停止 Cube.js(按CTRL+C
),然后使用 再次运行 Cube.js。npm run dev
您将在控制台中看到一条未提及开发模式的消息,并且开发者游乐场将不再出现在localhost:4000。
其次,我们来检查一下 Web 应用是否崩溃了。🙀应该是因为我们刚刚修改了安全密钥,没有提供正确的 JWT。curl
在控制台中重复执行该命令,我们会看到以下结果:
看起来没错。但那个“授权标头”到底是什么呢?它是一个 HTTP 标头Authorization
,Cube.js 用它来验证请求。我们没有通过curl
命令传递任何类似的东西,所以才会出现这样的结果。如果我们重新加载 Web 应用程序,就会看到以下内容:
确实,它也坏了。太好了,我们去修一下。
最后,让我们生成一个新的 JWT 并修复 Web 应用程序。您可以使用许多库来处理 JWT,但 Cube.js 提供了一种在命令行中生成令牌的便捷方法。运行以下命令,NEW_SECRET
并将第一步生成的密钥替换为:
npx cubejs-cli token --secret="NEW_SECRET" --payload="role=admin"
你会看到类似这样的内容:
输出提供了以下见解:
- 我们已经创建了一个新的 JWT:(
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJ1Ijp7fSwiaWF0IjoxNjE1MTY1MDYwLCJleHAiOjE2MTc3NTcwNjB9.IWpKrqD71dkLxyJRuiii6YEfxGYU_xxXtL-l2zU_VPY
您的令牌应该有所不同,因为您的密钥不同)。 - 它将在 30 天后到期(我们可以使用
--expiry
选项控制到期时间,但 30 天对于我们的目的来说已经足够了)。 - 它包含附加信息(
role=admin
),我们稍后将使用它进行授权。
我们可以访问jwt.io,复制粘贴我们的 token,然后检查它是否真的包含上述信息。只需将您的 JWT 粘贴到左侧的巨型文本框中即可。您将看到类似以下内容:
你是不是漏掉了“30天”这个参数?它们在属性中被编码成了exp
时间戳,你当然可以把这个值转换回人类可读的日期。你也可以通过将你的密钥粘贴到“验证签名”的文本输入框中,然后重新粘贴你的 JWT 来检查签名。
现在我们准备修复 Web 应用程序了。打开dashboard-app/src/App.js
文件。导入几行代码后,你会看到如下代码:
const API_URL = "http://localhost:4000";
const CUBEJS_TOKEN = "SOME_TOKEN";
const cubejsApi = cubejs(CUBEJS_TOKEN, {
apiUrl: `${API_URL}/cubejs-api/v1`
});
这几行代码配置了 Cube.js客户端库,使其在 处查找 APIlocalhost:4000
并传递特定的令牌。将其更改SOME_TOKEN
为刚刚生成并验证的 JWT,然后停止 Web 应用程序(按CTRL+C
),然后使用 再次运行它npm start
。我们将看到 Web 应用程序再次运行,并将我们刚刚添加到 API 的 JWT 与Authorization
标头一起传递:
为了仔细检查,我们可以在控制台中使用相同的标头运行相同的查询:
curl http://localhost:4000/cubejs-api/v1/load \
-H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE2MTUxNjUwNjAsImV4cCI6MTYxNzc1NzA2MH0.BNC8xlkB8vmuT0T6s1a5cZ3jXwhcHrAVNod8Th_Wzqw' \
-G -s --data-urlencode 'query={"measures": ["Orders.count"], "dimensions": ["Orders.status"]}' \
| jq '.data'
确保检查如果删除标题或仅更改令牌的单个符号,API 是否会返回错误,并且永远不会产生结果。
‼️我们能够添加身份验证并使用 JSON Web Tokens 保护 API。现在,只有传递了有效的 JWT 时,API 才会返回结果。要生成这样的 JWT,需要知道当前存储在文件中的密钥.env
。
现在,我们平静下来,是时候进行下一步并添加授权了🤿
步骤 2. 使用 JWT 授权
我们已经知道,授权的本质是允许用户根据其身份执行某些操作。我们如何实现这一点?
我们可以根据JWT 中的附加信息(或声明)来决定用户允许执行的操作。还记得吗,在生成 JWT 时,我们提供了有效负载role=admin
?我们将让 API 使用该有效负载来允许或限制用户的操作。
Cube.js 允许您通过安全上下文访问 JWT 的有效负载。您可以使用安全上下文来修改数据架构或支持多租户。
首先,让我们更新数据模式。在schema/Orders.js
文件中,您可以找到以下代码:
cube(`Orders`, {
sql: `SELECT * FROM public.orders`,
// ...
此 SQL 语句表示对该多维数据集的任何查询都将作用于public.orders
表中的所有行。假设我们要将其更改为如下形式:
- “admin”用户可以访问所有数据
- “非管理员”用户只能访问所有数据的一部分,例如,只有 10%
为了实现这一点,让我们schema/Orders.js
按如下方式更新文件:
cube(`Orders`, {
sql: `SELECT * FROM public.orders ${SECURITY_CONTEXT.role.unsafeValue() !== 'admin' ? 'WHERE id % 10 = FLOOR(RANDOM() * 10)' : ''}`,
// ...
这里发生了什么?让我们分解一下:
SECURITY_CONTEXT.role
允许我们访问有效负载的“role”字段的值。这样SECURITY_CONTEXT.role.unsafeValue()
我们就可以直接在 JavaScript 代码中使用该值并修改 SQL 语句。在此代码片段中,我们检查角色是否不等于“admin”值,这意味着“非管理员”用户发送了查询。- 在本例中,我们附加了一条新的
WHERE
SQL 语句,用于比较 的值id % 10
(即行的数字 ID 除以 10 的余数)和 的值FLOOR(RANDOM() * 10)
(即 范围内的伪随机数0..9
)。实际上,这意味着“非管理员”用户将能够查询所有数据的 1/10,并且随着 返回值的RANDOM()
变化,子集也会随之变化。 - 您还可以使用 和 直接检查有效负载中的值与表中的列
filter
。有关详细信息,requiredFilter
请参阅数据架构文档。
其次,让我们检查一下更新后的架构如何限制某些操作。猜猜看,如果你更新架构,停止 Cube.js(按CTRL+C
),然后使用 再次运行 Cube.js npm run dev
,最后重新加载我们的 Web 应用程序,会发生什么。
对,什么也没有!🙀 我们仍然使用 JWT 作为role=admin
有效负载,因此我们可以访问所有数据。那么,如何测试更新后的数据模式是否有效呢?
让我们生成一个不带有效载荷的新令牌,或者使用另一个角色npx cubejs-cli token --secret="NEW_SECRET" --payload="role=foobar"
,更新dashboard-app/src/App.js
文件,然后重新加载我们的 Web 应用程序。哇,现在它看起来……肯定比以前少了:
第三,让我们通过控制台进行检查。和之前一样,我们可以使用更新后的 JWT 运行以下命令:
curl http://localhost:4000/cubejs-api/v1/load \
-H 'Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiZm9vYmFyIiwiaWF0IjoxNjE1MTk0MTIwLCJleHAiOjE2MTUxOTc3NjEsImp0aSI6ImMxYTk2NTY1LTUzNzEtNDNlOS05MDg0LTk0NWY3ZTI3ZDJlZSJ9.FSdEweetjeT9GJsqRqEebHLtoa5dVkIgWX4T03Y7Azg' \
-G -s --data-urlencode 'query={"measures": ["Orders.count"], "dimensions": ["Orders.status"]}' \
| jq '.data'
效果非常好:
Cube.js 还提供了便捷的扩展点,可以使用安全上下文来实现多租户支持。最常见的情况是,你会使用为每个查询queryTransformer
添加强制租户感知过滤器。此外,你还可以根据安全上下文切换数据库及其架构和缓存配置。
‼️我们能够添加授权并使用 JWT 声明来控制数据访问。现在 API 可以识别用户的角色了。然而,目前唯一的 JWT 被硬编码到 Web 应用程序中,并在所有用户之间共享。
为了自动化为每个用户颁发 JWT,我们需要使用外部身份验证提供程序。让我们继续下一步,添加身份验证🤿
步骤3.通过Auth0进行身份验证
众所周知,身份识别的本质是询问用户身份。外部身份验证提供商可以处理此任务,允许用户通过各种方式(例如,他们的 Google 帐户或社交个人资料)进行身份验证,并提供互补的基础架构和库以与您的应用集成。
Auth0是一个领先的开发者身份管理平台,最近被规模更大的身份管理平台 Okta 收购。它能够安全地存储所有敏感用户数据,拥有便捷的 Web 管理面板,并为各种框架提供前端库。我们将使用 Auth0 与 React 的集成,但值得注意的是,Auth0 已与所有主流前端框架集成,例如 Cube.js。
除此之外,Auth0 还提供了许多高级功能:
- 用户角色——您可以拥有管理员、用户等。
- 范围——您可以为每个用户或每个角色设置特殊权限,例如,允许某些用户更改应用程序的设置或执行特定的 Cube.js 查询。
- 邮件——您可以连接第三方系统(如 SendGrid)来发送电子邮件:重置密码、欢迎等。
- 管理——您可以邀请用户、更改他们的数据、删除或阻止他们等。
- 邀请——您可以允许用户仅通过从 Auth0 发送的邀请电子邮件登录。
Auth0 让您轻松实现行业标准的OAuth 2.0 流程。OAuth 2.0 是一种经过验证的外部身份验证协议。其工作原理如下:
- 我们的应用程序将未经身份验证的用户重定向到外部身份验证提供程序。
- 提供商向用户询问其身份,验证身份,生成附加信息(包括 JWT),然后将用户重定向回我们的应用程序。
- 我们的应用程序假定用户现已通过身份验证并使用其信息。在我们的例子中,用户的 JWT 可以进一步发送到 Cube.js API。
所以,现在是时候使用 Auth0 进行身份验证并为每个用户颁发不同的 JWT 了。
首先,让我们设置一个 Auth0 帐户。您需要访问Auth0网站并注册一个新帐户。之后,导航到管理面板的“应用程序”页面。要创建与我们正在开发的应用程序匹配的应用程序,请点击“+ 创建应用程序”按钮,选择“单页 Web 应用程序”。完成!
进入“设置”选项卡,并记下以下字段:“域名”、“客户端 ID”和“客户端密钥”。我们稍后会用到它们的值。
然后向下滚动到“允许的回调 URL”字段并添加以下 URL 作为其值:http://localhost:3000
。Auth0 需要此 URL 作为额外的安全措施,以确保用户将被重定向到我们的应用程序。
点击最底部的“保存更改”,然后进入管理面板的“规则”页面。在这里,我们需要创建一个规则来为用户分配“角色”。点击“+ 创建规则”按钮,选择一个“空规则”,粘贴以下脚本,然后点击“保存更改”:
function (user, context, callback) {
const namespace = "http://localhost:3000";
context.accessToken[namespace] = {
role: user.email.split('@')[1] === 'cube.dev' ? 'admin' : 'user',
};
callback(null, user, context);
}
此规则将检查用户电子邮件中的域名,如果该域名等于“cube.dev”,则用户将获得管理员角色。您可以指定公司域名或其他任何条件,例如,user.email === 'YOUR_EMAIL'
仅将管理员角色分配给您自己。
最后一步是注册一个新的 Auth0 API。请导航至“ API ”页面,点击“+ 创建 API”,输入任意名称以及cubejs
“标识符”(稍后我们将此值称为“受众”)。
就这样,现在我们已经完成了 Auth0 设置。
其次,让我们更新 Web 应用程序。我们需要添加与 Auth0 的集成,使用重定向,并在用户重定向回后使用信息。
我们需要在文件中添加一些配置选项dashboard-app/.env
。请注意,有两个值需要从管理面板中的应用程序设置中获取:
REACT_APP_AUTH0_AUDIENCE=cubejs
REACT_APP_AUTH0_DOMAIN=<VALUE_OF_DOMAIN_FROM_AUTH0>
REACT_APP_AUTH0_CLIENT_ID=<VALUE_OF_CLIENT_ID_FROM_AUTH0>
此外,我们需要dashboard-app
使用以下命令将 Auth0 React 库添加到:
npm install --save @auth0/auth0-react
然后,我们需要用 包装 React 应用Auth0Provider
,它是一个配套组件,为树下的所有 React 组件提供 Auth0 配置。dashboard-app/src/index.js
按如下方式更新文件:
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom';
import ExplorePage from './pages/ExplorePage';
import DashboardPage from './pages/DashboardPage';
import App from './App';
+ import { Auth0Provider } from "@auth0/auth0-react";
ReactDOM.render(
+ <Auth0Provider
+ audience={process.env.REACT_APP_AUTH0_AUDIENCE}
+ domain={process.env.REACT_APP_AUTH0_DOMAIN}
+ clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
+ scope={'openid profile email'}
+ redirectUri={process.env.REACT_APP_AUTH0_REDIRECT_URI || window.location.origin}
+ onRedirectCallback={() => {}}
+ >
<Router>
<App>
<Route key="index" exact path="/" component={DashboardPage} />
<Route key="explore" path="/explore" component={ExplorePage} />
</App>
</Router>
+ </Auth0Provider>,
document.getElementById('root'));
最后的更改将应用于dashboard-app/src/App.js
实例化 Cube.js 客户端库的文件。我们将更新App
组件以与 Auth0 交互,并在 Auth0 返回相应的 JWT 时使用它们重新实例化客户端库。
首先,从中删除这些行dashboard-app/src/App.js
,我们不再需要它们了:
- const API_URL = "http://localhost:4000";
- const CUBEJS_TOKEN = "<OLD_JWT>";
- const cubejsApi = cubejs(CUBEJS_TOKEN, {
- apiUrl: `${API_URL}/cubejs-api/v1`
- });
之后,添加 Auth0 React hook 的导入:
+ import { useAuth0 } from '@auth0/auth0-react';
最后,更新App
功能组件以匹配以下代码:
const App = ({ children }) => {
const [ cubejsApi, setCubejsApi ] = useState(null);
// Get all Auth0 data
const {
isLoading,
error,
isAuthenticated,
loginWithRedirect,
getAccessTokenSilently,
user
} = useAuth0();
// Force to work only for logged in users
useEffect(() => {
if (!isLoading && !isAuthenticated) {
// Redirect not logged users
loginWithRedirect();
}
}, [ isAuthenticated, loginWithRedirect, isLoading ]);
// Get Cube.js instance with accessToken
const initCubejs = useCallback(async () => {
const accessToken = await getAccessTokenSilently({
audience: process.env.REACT_APP_AUTH0_AUDIENCE,
scope: 'openid profile email',
});
setCubejsApi(cubejs({
apiUrl: `http://localhost:4000/cubejs-api/v1`,
headers: { Authorization: `${accessToken}` },
}));
}, [ getAccessTokenSilently ]);
// Init Cube.js instance with accessToken
useEffect(() => {
if (!cubejsApi && !isLoading && isAuthenticated) {
initCubejs();
}
}, [ cubejsApi, initCubejs, isAuthenticated, isLoading ]);
if (error) {
return <span>{error.message}</span>;
}
// Show indicator while loading
if (isLoading || !isAuthenticated || !cubejsApi) {
return <span>Loading</span>;
}
return <CubeProvider cubejsApi={cubejsApi}>
<ApolloProvider client={client}>
<AppLayout>{children}</AppLayout>
</ApolloProvider>
</CubeProvider>;
}
export default App;
完成!现在,您可以停止 Web 应用程序(按CTRL+C
),然后使用 再次运行它npm start
。您将被重定向到 Auth0 并被邀请登录。使用您喜欢的任何方法(例如,Google)并返回到您的应用程序。您将看到以下内容:
看起来我们的应用程序从 Auth0 接收到一个 JWT,并将其发送到 API,然后失败并显示“无效令牌”。这是为什么呢?当然,因为 API 对我们通过 Auth0 识别用户并颁发 JWT 的决定一无所知。我们现在就修复这个问题。
第三,让我们配置 Cube.js 以使用 Auth0。Cube.js提供了便捷的内置集成,可与Auth0和Cognito集成,只需通过该文件即可配置.env
。将以下选项添加到此文件,<VALUE_OF_DOMAIN_FROM_AUTH0>
并用上面的相应值替换:
CUBEJS_JWK_URL=https://<VALUE_OF_DOMAIN_FROM_AUTH0>/.well-known/jwks.json
CUBEJS_JWT_ISSUER=https://<VALUE_OF_DOMAIN_FROM_AUTH0>/
CUBEJS_JWT_AUDIENCE=cubejs
CUBEJS_JWT_ALGS=RS256
CUBEJS_JWT_CLAIMS_NAMESPACE=http://localhost:3000
之后,保存更新的.env
文件,停止 Cube.js(按CTRL+C
),然后使用 再次运行 Cube.js。npm run dev
现在,如果您刷新 Web 应用程序,您应该会看到 API 返回的结果,完整的数据集或仅其中的 10%,具体取决于您的用户和您之前设置的规则:
‼️我们成功将 Web 应用程序和基于 Cube.js 的 API 与 Auth0 集成,Auth0 可作为外部身份验证提供程序。Auth0可识别所有用户并为其生成 JWT。现在,只有登录用户才能访问应用程序并执行对 Cube.js 的查询。巨大的成功!
剩下的唯一问题是:一旦我们有不同角色的用户与 API 交互,如何确保我们将来能够审查他们的操作?让我们看看 Cube.js 能提供什么 🤿
步骤 4. 使用审计日志进行问责
我们知道,问责的本质是能够了解不同用户执行了哪些操作。
通常,日志就是用于此目的的。何时何地写入日志?显然,我们应该为每次(关键)数据访问都写入日志。Cube.js 提供了queryTransformerqueryTransformer
,这是一个很好的扩展点。每个查询在处理之前都会运行其中的代码。这意味着您不仅可以写入日志,还可以修改查询,例如添加过滤器并实现多租户访问控制。
要为每个查询写入日志,请cube.js
按如下方式更新文件:
// Cube.js configuration options: https://cube.dev/docs/config
module.exports = {
queryTransformer: (query, { securityContext }) => {
const { role, email } = securityContext;
if (role === 'admin') {
console.log(`User ${email} with role ${role} executed: ${JSON.stringify(query)}`);
}
return query;
},
};
之后,停止 Cube.js(按CTRL+C
),使用 再次运行它npm run dev
,并刷新 Web 应用程序。在控制台中,您将看到如下输出:
当然,您可以使用更复杂的记录器,例如基于云的日志记录解决方案(如Datadog)。
‼️只需进行少量更改,我们就能够通过便捷的 Cube.js 扩展点为应用添加责任机制。此外,现在我们的应用中已经实现了 IAAA 的所有功能:身份识别、身份验证、授权和责任机制。JSON Web Token 会生成并传递给 API,基于角色的访问控制也已实现,并且外部身份验证提供程序会控制用户的登录方式。有了这些,多租户只需一行代码即可实现,几分钟内即可完成。
好了,朋友们! 🤿 希望你们喜欢这篇指南 🤗
最后,您可以做以下几件事:
- 前往GitHub 上的Cube.js repo并给它一颗星⭐️
- 在 Twitter、Reddit 或与朋友分享本指南的链接🙋♀️
- 在下面的评论中分享您对安全性、IAAA、Auth0 和 Cube.js 的见解、反馈和了解 ↓
PS:我要感谢 Aphyr 给予我灵感,让我在本指南开头引用了伪造的“乔治奥威尔”名言。
文章来源:https://dev.to/cubejs/multi-tenant-analytics-with-auth0-and-cube-js-the-complete-guide-31lo