测试脚本是用户写还是顾问写
Recently at our semi-regular architecture discussion group, we’ve been looking at the Auth0 service: what it is, how it works and when it might be a good idea to use it.
最近,在我们的半常规体系结构讨论小组中,我们一直在研究Auth0服务:它是什么,它如何工作以及何时使用它可能是一个好主意。
I’d like to focus on the last aspect, using Auth0 as a case-study for the more general problem of when to use external services in our microservices mix, and when to roll your own.
我想专注于最后一个方面,将Auth0用作案例研究,以解决以下更普遍的问题:何时在我们的微服务组合中使用外部服务,何时推出自己的服务。
On one hand, it’s totally reasonable not to write yet another UserService
which handles user authentication and authorization. It’s been done before, and we all like to reuse code. On the other, using an external service requires us to let go of the otherwise total control we have over our system.
一方面,完全不编写另一个处理用户身份验证和授权的UserService
是完全合理的。 之前已经做过,我们都喜欢重用代码。 另一方面,使用外部服务需要我们放弃对系统的其他全部控制。
It’s a tradeoff. How to decide, when to use one option or the other?
这是一个权衡。 如何决定何时使用一个选项或另一个选项?
验证码0 (Auth0)
Before diving into tradeoffs, let’s make a very quick introduction to Auth0, which we’ll use as our running example.
在权衡取舍之前,让我们快速介绍Auth0,我们将使用它作为运行示例。
Auth0 is a service which manages identities of the system’s users, providing both authentication and authorization.
Auth0是一项管理系统用户身份的服务,同时提供身份验证和授权。
Users can be authenticated in a variety of ways:
可以通过多种方式对用户进行身份验证:
- using traditional username/password 使用传统的用户名/密码
- through various social identity providers (Google, Facebook, Twitter, … )通过各种社会身份提供商(Google,Facebook,Twitter等)
- passwordless, with login links sent by e-mail 无密码,带有通过电子邮件发送的登录链接
- multi-factor (such as push, e-mail or voice notifications, one-time passwords)多种因素(例如推送,电子邮件或语音通知,一次性密码)
Auth0 can manage the database of the users for you, or you can connect your own datastore. There’s a number of integration options available, calling in either directly to the DB instance, or through an API. If the built-in options are not sufficient, you can always provide integration code yourself (in JavaScript).
Auth0可以为您管理用户的数据库,或者您可以连接自己的数据存储。 有许多可用的集成选项,可以直接调用数据库实例,也可以通过API调用。 如果内置选项不够用,您始终可以自己提供集成代码(使用JavaScript)。
For authorization, Auth0 offers role-based access control (again, with roles & permissions stored in Auth0’s database, or externally), as well as extension points to provide your own mechanisms (through rules, hooks and extensions).
对于授权,Auth0提供基于角色的访问控制(同样,角色和权限存储在Auth0的数据库中,或从外部存储),以及扩展点,以提供您自己的机制(通过rule , hook和extensions )。
You have the option to use Auth0-hosted login screens (a component that is very important for the end-user experience), which can be themed so that they match your site; or you can roll your own, and just call Auth0 APIs.
您可以选择使用Auth0托管的登录屏幕(对于最终用户体验非常重要的组件),可以对其进行主题设置,使其与您的网站相匹配; 或者您也可以自己滚动,只需调用Auth0 API。
Finally, Auth0 uses the OAuth2 standard for authorization, and the identity layer conforms to OpenID Connect (see also this introduction to the standards).
最后,Auth0使用OAuth2标准进行授权,并且身份层符合OpenID Connect (另请参见标准介绍)。
What does that mean? OAuth2 defines a number of flows, which should be used depending on the type of the client (if the client’s execution environment can be trusted or not), whether the client is an end-user or a machine, and what kind of session we want to establish (one-off, temporary or long-running).
这意味着什么? OAuth2定义了许多流,应根据客户端的类型(是否可以信任客户端的执行环境),客户端是最终用户还是机器以及需要哪种会话来使用多个流建立(一次性,临时或长期运行)。
Auth0 endpoints behave as specified by OAuth2, hence we know what to expect when interacting with them. Which OAuth2 flow to use depends on the concrete use case, which can be such as:
Auth0端点的行为与OAuth2指定的行为相同,因此我们知道与它们进行交互时会发生什么。 使用哪种OAuth2流取决于具体的用例,例如:
- regular web app, with a server-side component 常规Web应用程序,带有服务器端组件
- single-page-app, with client-side sessions only单页应用程序,仅具有客户端会话
- mobile apps行动应用程式
- machine-to-machine (service-to-service)机器对机器(服务对服务)
- single-sign-on (SSO) 单点登录(SSO)
OAuth2 relies on signed tokens being passed between interested parties. The tokens that Auth0 creates are JWT tokens, hence in an almost-human readable JSON format. That’s yet another standard, which you can encounter when implementing the security layer of your application.
OAuth2依赖于相关方之间传递的签名令牌。 Auth0创建的令牌是JWT令牌,因此采用几乎人类可读的JSON格式。 那是实现应用程序安全层时可能会遇到的另一个标准。
Finally, what are the alternatives to Auth0? It’s not the only service implementing such functionality. Other options that you might want to check out if you are surveying the authentication landscape are:
最后,什么是Auth0的替代品? 它不是实现此类功能的唯一服务。 如果您正在调查身份验证环境,则可能要检查的其他选项包括:
as-a-service: Okta, Amazon Cognito, onelogin, PingIdentity, FusionAuth
即服务: Okta , Amazon Cognito , onelogin , PingIdentity , FusionAuth
self-hosted: KeyCloak, Gluu, FreeIPA
自托管: KeyCloak , Gluu , FreeIPA
外部微服务 (External microservices)
Modern systems are typically composed of multiple services of various sizes. Deployments vary, from Kubernetes on one end, through Heroku and bare metal on the other. However and wherever the services are deployed, they usually communicate using HTTP APIs, or at least offer the possibility to communicate with them this way.
现代系统通常由各种大小的多种服务组成。 部署各不相同,从一端的Kubernetes到另一端的Heroku和裸机。 但是,无论在何处部署服务,它们通常都使用HTTP API进行通信,或者至少提供以这种方式与它们进行通信的可能性。
Hence, replacing one of the (micro)services with an external API shouldn’t create huge changes in the system architecture. Still, there’s a number of factors to consider when deciding “if” and choosing “which” service to bet on.
因此,用外部API替换一种(微)服务不应在系统体系结构中造成巨大的变化。 不过,在决定“如果”并选择“哪种”服务时,仍需要考虑许多因素。
可更换性 (Replaceability)
The absolute first thing to consider is how easy it is to replace a potential external microservice. Nobody likes vendor lock-in: integrating too much with an external provider, we are then forced to use their services for better or worse, as the cost of switching is too high or would cause too much disruption.
首先要考虑的是更换潜在的外部微服务有多么容易。 没有人喜欢厂商锁定:与外部供应商进行过多的整合,然后由于转换成本太高或会导致过多的中断,我们被迫使用其服务的好坏。
Hence, we need to check whether the APIs that the service exposes conform to open, or at least “emergent” standards — and if these open standards are really used throughout the industry. Does the competition support the same standards as well? That is, what would it take to switch to another vendor?
因此,我们需要检查服务公开的API是否符合开放标准,或者至少符合“新兴”标准,以及这些开放标准是否在整个行业中得到了真正的使用。 比赛是否也支持相同的标准? 也就是说,切换到另一个供应商将需要什么?
How does Auth0 score in replaceability? On the plus sides, we’ve got the OAuth2 and OpenID Connect standards, which are not only a codified specification, but they are indeed used across the industry. Basing your authentication flows on OAuth2 will ensure that you will be able to switch both to other identity service vendors, or to roll your own: there’s a lot of OAuth2-supporting open-source projects (and a couple of close-source ones probably as well).
Auth0在可替换性方面如何得分? 从好的方面来说,我们拥有OAuth2和OpenID Connect标准,它们不仅是规范化的规范,而且确实在整个行业中使用。 基于OAuth2的身份验证流将确保您将能够切换到其他身份服务供应商,也可以自己滚动:有很多OAuth2支持开源项目(以及几个封闭源代码项目)好)。
However, it’s not all roses. Auth0 does extend or adjust the standard slightly. For example to implement role-based access control, the permissions calculated from user roles are returned in the permissions
claim of the access tokens, which are non-standard. While adding custom claims is something the standard does allow of course, in the event of migration, you’d need to replicate that functionality.
但是,不是所有的玫瑰。 Auth0确实会稍微扩展或调整标准。 例如,要实现基于角色的访问控制,将从用户角色计算出的permissions
返回到非标准的访问令牌的permissions
声明中。 虽然添加自定义声明是标准所当然允许的,但是在迁移的情况下,您需要复制该功能。
Another point where you might encounter some vendor lock-in are the SDKs. Auth0 provides a rich set of libraries which allow interacting with the service from a number of programming languages, both to perform client or server-side authentication, parse access tokens and interact with the Auth0 management API. That’s a very useful feature when developing applications: however, you should be aware that using them might tie you to the Auth0 service itself. If you were to create your own user service, you might have to amend the code to use a different or custom SDK.
您可能会遇到一些供应商锁定的另一点是SDK。 Auth0提供了一组丰富的库,这些库允许通过多种编程语言与服务进行交互,以执行客户端或服务器端身份验证,解析访问令牌并与Auth0管理API进行交互。 在开发应用程序时,这是一个非常有用的功能:但是,您应该注意,使用它们可能会使您与Auth0服务本身联系在一起。 如果要创建自己的用户服务,则可能必须修改代码以使用其他或自定义的SDK。
安全 (Security)
Since we are delegating some part of the system’s data and functionality to an external provider, security should be one of our main concerns. Looking through the company blog, documentation and regulatory compliance (GDPR/CCPA/CalOPPA/…) should provide some initial background into how seriously a given service takes security.
由于我们将系统数据和功能的某些部分委派给外部提供商,因此安全性应该是我们主要关注的问题之一。 通过浏览公司博客,文档和法规遵从性( GDPR / CCPA / CalOPPA /…)应该为某种特定服务对安全性的重视程度提供一些初步背景。
Further, we might want to investigate previous incidents (if any). If there were security incidents, this doesn’t yet eliminate the service — we should check how the incident was handled, if a post-mortem was published, how transparent the company was about things that went wrong, and what are the conclusions for the future.
此外,我们可能希望调查以前的事件(如果有)。 如果发生安全事件,这还不能消除服务-我们应该检查事件的处理方式,是否发布事后调查,公司对发生错误的事情的透明度以及对这些事件的结论是什么?未来。
Even the presence of a simple status dashboard gives some insight into how the company communicates with their users.
即使是简单的状态仪表板,也可以让您深入了解公司如何与用户进行沟通。
Auth0 is in a special position, as their service itself deals with security, so in this case, security of the service is double important. Take a look at their compliance and status pages.
Auth0处于特殊位置,因为它们的服务本身处理安全性,因此在这种情况下,服务的安全性具有双重重要性。 查看其合规性和状态页面。
潜伏 (Latency)
When services are deployed together, they usually enjoy the benefits of communicating within a local or almost-local network. This changes when you use an external service. If it’s being used a lot, what is the additional price per request that you have to pay for using the service?
将服务一起部署时,它们通常会享受在本地或几乎本地网络内进行通信的好处。 当您使用外部服务时,这会改变。 如果使用率很高,使用该服务必须为每个请求支付的额外价格是多少?
And the price I’m talking about here is not measured in USD, but in milliseconds — every additional millisecond of latency might directly translate to user happiness (and we end up with USD again). Using an external service might mean an additional network hop or multiple hops — e.g. if the service looks up data in our database, at our datacenter, through an extension point.
而且我在这里所说的价格不是以美元衡量的,而是以毫秒为单位的–每增加一毫秒的延迟就可能直接转化为用户的幸福感(而我们最终又得到了美元)。 使用外部服务可能意味着一个额外的网络跃点或多个跃点-例如,如果该服务通过扩展点在我们的数据中心的数据库中查找数据。
Hence, does the external service provider offer endpoints in various regions? Quite often we have US and EU-based servers, but what about other continents — what if we want to deploy the rest of our services in Australia, Asia, Africa or non-US America?
因此,外部服务提供商是否在各个地区提供端点? 我们经常有基于美国和欧盟的服务器,但是其他大洲呢?如果我们想在澳大利亚,亚洲,非洲或非美国地区部署其余服务,该怎么办?
You might also want to check if the external service is deployed on the infrastructure of a specific cloud provider. If it’s e.g. AWS, you’ll get great connectivity when hosting your services in the same AWS region, but probably worse when using a different region or GCP/Azure.
您可能还需要检查外部服务是否已部署在特定云提供商的基础架构上。 如果是AWS,则在同一AWS区域中托管服务时将获得良好的连接性,但在使用其他区域或GCP / Azure时,连接性可能会更差。
In case of Auth0, all of their public infrastructure is hosted on AWS. They offer four regions: US, US-2, EU and AU. It’s also worth noting that you can choose a private cloud deployment option (which of course is more expensive).
对于Auth0,其所有公共基础结构都托管在AWS上。 它们提供四个地区:美国,US-2,欧盟和非盟。 还值得注意的是,您可以选择私有云部署选项(这当然更昂贵)。
可扩展性 (Extensibility)
External microservices put a focus on making the most common use cases easy to configure and pleasant to use, so that they can attract the widest possible audience (it’s a business which has to make a profit, after all).
外部微服务专注于使最常见的用例易于配置和易于使用,从而吸引尽可能多的受众(毕竟,这是一项必须获利的业务)。
This might mean that, like many web frameworks in popular programming languages, bootstraping a project is rapid and leads to a very effective initial iteration. Even implementing 90% of the required functionality might be a breeze. However, the pain and hacking starts when we are left with the remaining non-standard 10%.
这可能意味着,就像流行的编程语言中的许多Web框架一样,快速引导项目并导致非常有效的初始迭代。 即使实现90%的所需功能也很容易。 但是,当我们剩下剩下的非标准10%时,痛苦和黑客就开始了。
That’s where extensibility of the external service comes into play. What are the available extension points? Can you plug-in into each step in the lifecycle of a managed data entity or into each step of a request? What are the constraints on the integrations?
这就是外部服务的可扩展性发挥作用的地方。 有哪些可用的扩展点? 您可以插入到托管数据实体生命周期的每个步骤中还是插入请求的每个步骤中? 集成有哪些约束?
Looking at Auth0, there are rules, which run after a user is authenticated. There are also hooks, which run at pre-defined extension points; some of them run synchronously, some asynchronously. Finally, there are extensions, which allow to integrate with third-party applications such as logging services (which is an important aspect as well!).
查看Auth0,其中有rules ,这些规则在用户通过身份验证后运行。 还有钩子,它们在预定义的扩展点运行; 其中一些同步运行,一些异步运行。 最后,还有一些扩展,可以与第三方应用程序集成,例如日志记录服务(这也是一个重要方面!)。
As already mentioned, the custom extension code is written using JavaScript, and can interact with any API, and use one of the many available libraries.
如前所述,自定义扩展代码是使用JavaScript编写的,可以与任何API交互,并可以使用许多可用库之一。
We’ve successfuly started using Auth0 at SoftwareMill and the available extension points have been sufficient so far. But more projects and more production experience is needed to verify if anything important is missing.
我们已经成功开始在SoftwareMill中使用Auth0,到目前为止,可用的扩展点已经足够了。 但是需要更多的项目和更多的生产经验来验证是否缺少任何重要的东西。
成本 (Cost)
In the end, it all boils down to costs. For systems with low or medium traffic, using an external service will almost always be way cheaper than the cost of developing a custom implementation.
最后,一切都归结为成本。 对于流量较低或中等的系统,使用外部服务通常总是比开发自定义实现的成本便宜。
It’s worth noting, however, that usage costs (which are typically sized proportionally to the number of users or API requests) are not the only cost associated with using an external service. There’s still development costs, which include:
但是,值得注意的是,使用成本(通常与用户或API请求的数量成比例地确定)不是与使用外部服务相关的唯一成本。 仍有开发成本,其中包括:
- configuring the service 配置服务
- integrating the system’s codebase with the service将系统的代码库与服务集成
Depending on the quality of the documentation, the UX of the administration panel, and automation options (see also below), this might require smaller or larger amounts of training. Same with integration costs — a good SDK which matches the ecosystem (both language and libraries) that you are using to develop the rest of the system will save a lot of development time. However, as already mentioned, it might also tighten vendor lock-in.
根据文档的质量,管理面板的UX和自动化选项(另请参见下文),这可能需要较少或大量的培训。 与集成成本相同–良好的SDK与您用于开发系统其余部分的生态系统(语言和库)相匹配,将节省大量开发时间。 但是,如前所述,它也可能会加强供应商锁定。
In case of Auth0, pricing is something you’ll have to calculate for your use-case yourself, I’ll refrain from giving any advice here. As for the development costs when using the service — OAuth2 and OpenID are both non-trivial standards to understand, and they take time to learn. However, this knowledge is largely vendor-independent. Getting to know Auth0 concepts still takes some time (the distinction between tenants, apps, etc. for example), but given solid background in open authentication standards, this shouldn’t be too challenging.
在使用Auth0的情况下,您必须自己为用例计算价格,在此我将不提供任何建议。 至于使用该服务时的开发成本,OAuth2和OpenID都是不容易理解的标准,需要花费一些时间来学习。 但是,此知识在很大程度上与供应商无关。 认识Auth0概念仍然需要一些时间(例如,租户,应用程序等之间的区别),但是考虑到开放式身份验证标准的扎实背景,这应该不会太有挑战性。
自动化 (Automation)
When initially setting up the service, we’ll probably be happy with a nice-looking administration UI, clicking around exploring the various options and discovering the features.
最初设置服务时,我们可能会对外观美观的管理UI感到满意,单击浏览各种选项并发现功能。
However later, as we dig deeper and start developing our system, integrating with the given external service more seriously, clicking in the UI will stop being fun and become frustrating instead. Moreover, if we are configuring multiple environments, doing this manually will not only be a waste of time, but also very error-prone.
但是,稍后,随着我们进行更深入的研究并开始开发我们的系统,更认真地与给定的外部服务集成,在UI中单击将变得不再有趣,而变得令人沮丧。 此外,如果我们要配置多个环境,则手动执行此操作不仅会浪费时间,而且很容易出错。
That’s why it’s crucial that a service exposes all of their features through an API. AWS got this right, and even went a step further: the UI sometimes gives only some of the functionality, that is available when using the API programmatically. And that’s the right approach. Having a well-documented configuration API is a must. If there are ready-to-use SDKs ready in popular programming languages, even better.
这就是为什么服务通过API公开其所有功能至关重要。 AWS做到了这一点,甚至走得更远:UI有时仅提供某些功能,这些功能以编程方式使用API时可用。 这是正确的方法。 必须拥有记录良好的配置API。 如果有流行的编程语言提供的即用型SDK,那就更好了。
But that’s only one aspect of automation. Another is infrastructure-as-code tools, such as Terraform, Ansible or Chef. Using these we can describe the target configuration of the external system, which is typically stored in one or multiple files. These files can then be part of our version control system, and the configuration can be partially dynamic, depending e.g. on the target environment. A separate tool-dependent process then applies the configuration to the external service, adjusting the “current” state with the “desired” state.
但这仅仅是自动化的一个方面。 另一个是基础设施即代码工具,例如Terraform , Ansible或Chef 。 使用这些我们可以描述外部系统的目标配置,该目标配置通常存储在一个或多个文件中。 这些文件可以成为我们版本控制系统的一部分,并且配置可以部分动态,具体取决于目标环境。 然后,依赖于工具的单独过程将配置应用到外部服务,以“期望”状态调整“当前”状态。
Auth0 exposes a comprehensive management API, so everything looks good on this front. It also allows connecting a git repository storing configuration, which is automatically applied on push. The configuration files in the repository must follow a pre-defined structure. Finally, there’s a Terraform Auth0 provider, which can be used to fully manage Auth0 configuration.
Auth0公开了全面的管理API ,因此在这方面一切都看起来不错。 它还允许连接git存储库存储配置,该配置会在推送时自动应用。 存储库中的配置文件必须遵循预定义的结构。 最后,有一个Terraform Auth0提供程序,可用于完全管理Auth0配置。
测验 (Testing)
Let’s not forget about testing! Since we are externalising part of our system, we will no longer be able to run everything on our laptop. But that ship might have sailed long ago, if our system is truly composed of a non-trivial amount of microservices. We’ll need service stubs for local development anyway.
让我们不要忘记测试! 由于我们正在外部化系统的一部分,因此我们将不再能够在笔记本电脑上运行所有内容。 但是,如果我们的系统真正由数量可观的微服务组成,那艘船可能早就航行了。 无论如何,我们将需要用于本地开发的服务存根。
That said, we’ll still need to configure the external system in a couple of copies, to setup production/staging/development environments. As mentioned above, automation is crucial here. The external service should make it easy enough to quickly and dynamically create a fresh copy.
也就是说,我们仍然需要将外部系统配置成几个副本,以设置生产/过渡/开发环境。 如上所述,自动化在这里至关重要。 外部服务应使其足够容易,以快速动态地创建新副本。
加起来 (Summing up)
As we move towards fine-grained single-responsibility microservices, it’s increasingly feasible to use an external service for the “standard” parts of each system.
随着我们朝着细粒度的单责任微服务迈进,对每个系统的“标准”部分使用外部服务变得越来越可行。
We’re commonly doing this with logging, using services such as Loggly or DataDog. We’re using managed databases, be it on AWS, Heroku or database-vendor-specific solutions. We’re storing binaries on S3. Externalising user authentication and authorization might be a good candidate as well.
我们通常使用Loggly或DataDog之类的服务进行日志记录。 我们正在使用托管数据库,无论是在AWS,Heroku还是特定于数据库供应商的解决方案上。 我们在S3上存储二进制文件。 外部化用户身份验证和授权也可能是一个不错的选择。
There still might be cases where developing your own UserService
will be a better option — if you have very non-standard requirements, need to replace an existing API, or due to legal/regulatory requirements. However, chances are high that for quite a lot of systems using an external service will be the most cost-efficient and future-proof solution.
在某些情况下,开发自己的UserService
可能是一个更好的选择-如果您有非常非标准的要求,需要替换现有的API或出于法律/法规要求。 但是,对于使用外部服务的许多系统而言,这是最经济高效且面向未来的解决方案的机会很高。
翻译自: https://blog.softwaremill.com/never-write-a-userservice-again-d771e10265d
测试脚本是用户写还是顾问写