具有 Prisma 和 PropelAuth 的多租户 Next.js 应用程序
<h1 id="具有-prisma-和-propelauth-的多租户-nextjs-应用程序">具有 Prisma 和 PropelAuth 的多租户 Next.js 应用程序</h1><p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/0a409041c32b58394b13f5b1713bcc15.jpg"></p>
<h1 id="多租户应用程序">多租户应用程序</h1>
<p>在软件世界中,一个 <strong>租户</strong> 是一起使用产品的用户的集合。这可以少至单个用户,也可以多至大型企业。</p>
<p><strong>单租</strong> 是每个租户拥有自己的专用应用程序、数据库和基础设施的时候。这与 <strong>多租户</strong> 租户共享资源的地方。</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/2a0c059e0bc25f19036f35ff9ed2b8ae.jpg"></p>
<p>在任何一种情况下,一个共同的目标是将租户彼此隔离,这样他们就无法查看或修改彼此的数据。在单租户模型中,这很自然地发生,因为它们是完全分离的。在多租户模型中,这必须在应用程序或数据库级别强制执行。</p>
<p>即使有这么多的工作,为什么还要选择多租户应用程序?有一些明显的优势,比如不需要为每个客户维护或支付单独的基础设施。在这篇文章中,我们将展示如何使用 Next.js、Prisma 和 PropelAuth 构建多租户应用程序。</p>
<h1 id="我们要建造什么">我们要建造什么?</h1>
<p>我们将构建一个简单的 B2B 应用程序,每个用户都可以在租户内发布“帖子”。租户中的所有用户都应该能够阅读他们自己的帖子,并且租户之外的任何人都应该能够查看或访问它们。</p>
<p>在我们的 B2B 产品中,我们的租户是 <strong>组织</strong> ,我们稍后会看到更多。最终产品将如下所示:</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/75dfc294be9de694938fef4938a8e36e.jpg"></p>
<p>租户中的每个人都可以查看和发布只有他们可以看到的帖子。</p>
<h1 id="我们如何处理多个租户中的用户">我们如何处理多个租户中的用户?</h1>
<p>如果用户只在一个租户中,那么我们应该显示哪个租户的数据非常明显。但是当用户在多个租户中时会发生什么?</p>
<p>在实践中,这个问题经常出现。 GitHub 允许用户同时在多个组织中。同样,您可以在任意数量的 Slack 工作区中使用,并且您可以拥有多个在其间切换的 Google/Twitter 帐户。</p>
<p>你如何解决这个问题取决于你的应用程序,但有几个思想流派:</p>
<ul>
<li>每个租户都应该有自己唯一的 URL(例如 <strong>https://tenant.example.com</strong> 或者 <strong>https://example.com/tenant/</strong> )</li>
<li>只有一个网址(例如 <strong>https://app.example.com</strong> ) 并且用户使用下拉菜单或类似的东西选择要在 UI 中查看的租户。</li>
</ul>
<p>有关您选择哪种方法的更多详细信息,我们已经写了每种方法的优缺点 这里 .在此示例中,我们假设用户可以通过转到其租户名称在路径中的 URL 来查看其租户的帖子,例如 <strong>https://example.com/tenant/</strong></p>
<h1 id="在-nextjs-中检查路径参数">在 Next.js 中检查路径参数</h1>
<p>首先,我们将创建一个 React 钩子,它可以通过检查 URL 的路径来告诉我们用户当前正在查看哪个租户。首先创建一个新的 Next.js 应用程序</p>
<p>Next.js 支持 动态路线 这使得这非常简单。一个文件创建于<code> pages/org//posts.tsx</code> 将响应任何路线,例如<code> /org/某事/帖子</code> 或者<code> /org/somethingelse/posts</code> .</p>
<p>如果我们在该文件中编写以下代码:</p>
<p>我们将能够根据用户的 URL 查看用户正在尝试查看的租户</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/41ed99cd62bef37ec92b3f38eb74f926.jpg"></p>
<h1 id="验证用户是否在给定租户中">验证用户是否在给定租户中</h1>
<p>我们目前没有用户的概念,更不用说租户了。我们不希望任何人都能够查看“123”组织或任何组织的所有帖子。</p>
<p>这是哪里 PropelAuth PropelAuth 是为多租户和 B2B 用例设计的身份验证服务。它包括供每个租户/组织自行管理的自助服务 UI。我们的用户将能够注册、创建租户并邀请他们的同事,而无需我们为其编写任何代码。</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/93bfd65239d86d9f7db530117fa48c29.jpg"></p>
<p>这 入门指南 将引导您了解如何配置用户的身份验证体验,从 UI 的外观到您为用户提供的登录选项,再到这些用户的组织选项。这个 演示 显示了一些可用的配置选项。</p>
<p>配置完成后,我们的用户现在可以自行注册、登录、创建租户等。我们所要做的就是检查他们是否在他们试图访问的租户中。我们可以做到这一点 PropelAuth 的 React 库 .</p>
<p>那么,在<code> 页面/_app.ts</code> ,我们用 AuthProvider 包装我们的应用程序。 AuthProvider 会联系我们的 PropelAuth 实例并获取我们当前用户的元数据(如果他们已登录)。您将需要您的 <strong>授权网址</strong> 您可以在前端集成下的仪表板中找到它。</p>
<p>就是这样,我们现在可以在应用程序的任何地方访问我们的用户信息。我们可以使用高阶函数,例如<code>[ withAuthInfo](https://docs.propelauth.com/reference/frontend-apis/react/#withauthinfo)</code> 或钩子之类的<code>[ 使用AuthInfo](https://docs.propelauth.com/reference/frontend-apis/react/#useauthinfo)</code> .让我们更新我们的<code> /org//posts.tsx</code> 文件以检查用户是否在 orgName 中。</p>
<p>对于我们的用户不是其成员的任何租户/组织,他们将收到“未找到”错误。</p>
<p>请注意,这只是前端检查。前端检查很重要 <strong>可用性</strong> 但不是 <strong>安全</strong> .如果我们为一个租户获取所有帖子,然后决定不在前端显示它,那么任何用户都可以检查他们的网络流量来查看数据。</p>
<p>我们仍然希望对此进行检查,以便为出现在错误页面上的用户提供合理的错误消息,但现在让我们看看如何制作 <strong>认证</strong> 对我们后端的请求。</p>
<h1 id="向我们的后端发出经过身份验证的请求">向我们的后端发出经过身份验证的请求</h1>
<p>为了制作一个 <strong>认证</strong> 请求,我们需要为我们的后端提供一些 <strong>可验证的</strong> 证明我们的用户就是他们所说的人的信息。 PropelAuth 提供 访问令牌 为此,我们的后端可以在不发出任何外部请求的情况下进行验证。我们已经从 <strong>useOrgMembershipByPathParam</strong> 所以让我们使用它。</p>
<p>当我们的 orgMembership 更改并呈现页面时,我们基本上调用 fetchPosts。为简洁起见,我们将省略帖子本身的样式。至于 fetchPosts 方法本身:</p>
<p>由于我们的后端 API 不是面向客户的,因此我们可以根据需要传入 orgId。正如我们上面所做的那样,我们可以使用查询参数,继续使用路径参数,将其放在发布请求的正文中,等等。</p>
<p>保存帖子非常相似,我们只需要一个用于文本本身的基本文本区域,然后我们将使用以下函数来保存帖子:</p>
<p>我们现在在前端拥有了我们需要的一切——唯一的问题是我们还没有后端。让我们使用 Next.js 的内置 API 路由来解决这个问题。</p>
<h1 id="使用-nextjs-api-路由和-prisma-创建我们的后端">使用 Next.js API 路由和 Prisma 创建我们的后端</h1>
<p>如果没有某种方式将帖子存储在某个地方,我们的后端将不会很有用。我们将设置 Prisma,一种流行的 TypeScript ORM,它将管理我们所有的数据库操作,包括迁移。</p>
<p>之后,你的 repo 中应该有一个新文件,<code> 棱镜/schema.prisma</code></p>
<p>这是我们将定义我们的模式的地方。架构本身相当简单。我们想要一个包含一些要显示的文本的帖子。帖子应与租户(组织)和用户相关联。</p>
<p>没什么太花哨的,每个帖子都有一个默认为新 UUID 的 post_id。它包含租户 (org_id) 和用户 (user_id) 的 ID。最后,它包含我们的文本。现在我们可以迁移我们的数据库,这意味着创建或更新它到最新的模式:</p>
<p>将来,如果我们想添加更多字段,例如时间戳,我们可以更新我们的模式,然后 migrate 会处理剩下的事情。</p>
<h1 id="使用-nextjs-api-路由">使用 Next.js API 路由</h1>
<p>Next.js API 路由允许您在 Next.js 中创建自己的 API。正如 Next.js 自己所说:</p>
<p>文件夹内的任何文件<code> 页面/api</code> 映射到<code> /api/*</code> 并将被视为 API 端点而不是<code> 页</code> .它们是仅限服务器端的捆绑包,不会增加您的客户端捆绑包大小。</p>
<p>创建一个新文件<code> 页面/api/post.ts</code> 它将响应请求<code> /api/发布</code></p>
<p>我们需要检查<code> 方法</code> 首先决定如何处理请求:</p>
<p>我们将通过列出组织/租户的所有帖子来响应 GET 请求,就像前端期望的那样:</p>
<p>你可能会注意到 <strong>邮政</strong> 进口自 <strong>@prisma/客户</strong> 是我们的 Post 类型,包括 post_id、org_id、user_id 和 text。</p>
<p>但是有几个问题,首先,我们如何知道我们应该为哪个租户/组织获取帖子?此外,我们如何知道发出 API 请求的人是否在该租户中?</p>
<p>早些时候,我们从前端传递了两条信息:</p>
<ul>
<li>org_id 作为查询参数</li>
<li>访问令牌是 <strong>可验证的</strong> 通过我们的后端</li>
</ul>
<p>PropelAuth 除了提供前端库外,还提供后端库来验证这些令牌。我们将按照 Next.js API 路由入门指南 ,这让我们使用<code> @propelauth/快递</code> 图书馆:</p>
<p>通过使用仪表板中提供给我们的参数调用 initAuth 来设置我们的后端:</p>
<p>最后,我们将调用 要求组织成员 ,这将确保提供了有效的访问令牌并且用户在指定的组织中。综上所述,我们的 getHandler 应该如下所示:</p>
<p>我们将通过创建一个新帖子并使用相同的函数来响应 POST 请求:</p>
<p>在编写此函数时,您可能会注意到 Prisma 的最佳功能之一——您可以在此处获得自动完成和类型安全:</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/9a5b8dfece25cc64c540b691a956f95e.jpg"></p>
<p>就这样!我们现在拥有测试产品所需的一切。</p>
<h1 id="测试">测试</h1>
<p>我们先测试一下,我们不能查看其他租户的数据。如果我们前往任意租户的帖子页面,我们将看到“未找到”消息。</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/0c67d253f4a5a7681b5e06ba2fc0fdb4.jpg"></p>
<p>但是,就像我们之前说的,这只是一个前端检查。让我们使用 cURL 来确保我们不能直接向后端发出请求:</p>
<p>正如预期的那样,我们得到一个 401,因为我们没有指定一个访问令牌来表示我们是一个有效的用户。如果我们确实传递了一个有效的访问令牌,但仍然传递了一个 <strong>org_id</strong> 如果我们不在,我们将收到 403,表示我们无权查看该组织的数据。</p>
<p>最后,我们可以在 PropelAuth 提供的托管 UI 中创建一个组织:</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/964f88ee5795ff7315d84a6c298f57ca.jpg"></p>
<p>发表帖子并验证我们是否可以查看它:</p>
<p><img src="https://qanswer-1251273400.cos.ap-hongkong.myqcloud.com/post_images/75dfc294be9de694938fef4938a8e36e.jpg"></p>
<h1 id="概括">概括</h1>
<p>我们已经构建了多租户 B2B 应用程序所需的大部分构建块。我们的用户可以自己注册、创建租户和管理租户。我们的后端由高度可扩展的 Next.js API 路由提供支持。借助 Prisma,我们在应用程序和数据库之间实现了数据库迁移和类型安全。</p>
<p><em>更多内容在</em> ** <em>纯英语.io</em>** <em>.注册我们的</em> ** <em>免费每周通讯</em>** <em>.跟着我们</em> ** <em>推特</em>** , ** <em>领英</em>** ** <em>,</em><strong> ** <em>YouTube</em>** ** <em>, 和</em></strong> __<strong><em>不和谐</em></strong> ** <em>.</em>** <em>对增长黑客感兴趣?查看</em> ** <em>电路</em>** ** <em>.</em>**</p>
<p>版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明</p>
<p>本文链接:https://www.qanswer.top/23188/58170918</p><br><br>
来源:https://www.cnblogs.com/amboke/p/16673763.html
頁:
[1]