【51CTO.com快译】从概念上说,身份验证是指识别出用户是谁,而授权是指确定某个已被认证用户具有的何种访问级别。不知您是否注意到:由于分布式应用往往难以实现强大、且集中式管理所需的身份验证和授权(Authentication and Authorization,A&A)策略,因此在微服务的实际应用中,您可能时常会遇到验证和授权的实施问题。下面,我将和您探讨开放式策略代理(Open Policy Agent,OPA)是如何协助简化授权问题的。
为简单起见,我创建了一个带有多个微服务应用示例。通过其基本的用户界面,我们可以在其中执行各项操作,并查看产生的结果。该应用将向您展示开放式策略代理是如何处理各种授权方案的。
应用示例
在此,我们以销售团队常用的、为客户制定报价的CPQ(配置、定价和报价)应用(https://en.wikipedia.org/wiki/Configure,_price_and_quote)为例,定义并创建了如下角色:
- 销售–作为销售人员,他们可以为客户创建并更新报价。但是不能删除报价。
- 销售支持–作为支持人员,他们可以查看所有报价,但不能编辑任何报价。
- 销售管理员–作为管理员,他们可以查看所有报价,但不能编辑或创建任何报价。不过,他们可以根据清理需求去删除报价。
鉴于本文仅专注于授权环节,在此,我们假设用户已经通过了身份验证,并且持有有效的JSON Web令牌(JWT)。而且每个API请求,都会在请求的头部包含该JWT。
该示例应用在GitHub的下载链接为--https://github.com/gchaware/opa-ms-sample/tree/master。请根据README的说明,逐步进行安装,并通过URL--http://
执行授权
根据上述角色分配,我们授权
- 销售团队能够创建新的报价,查看报价,以及更新现有报价。
- 销售支持团队只能查看报价,不能编辑或创建它们。
- 销售管理团队既能够查看报价,又能够删除它们。
如上图所示的UI显示了多个按钮,每个按钮都代表用户的一项操作。根据用户选择使用的角色,UI将会及时反馈创建、编辑或删除商品操作成功与否的结果。
上图展示了该应用程序带有两个微服务:Offer(报价)和Customer(客户)。通过将API向外界公布,NGINX反向代理能够截获每个API请求,并通过请求授权服务,来验证是否允许用户执行该请求的相关操作。
在此,我们使用NGINX的auth_request指令,来截获传入的API调用。其中,每个API调用都有一个包含了JWT头部的授权。而所有用户的基本信息,包括其角色都被包含在JWT中。在此,该授权服务带有两个容器:
- Authorization(授权)–作为已定制的授权服务,可用于接收请求,并为下面的Open Policy Agent创建经过格式化的输入请求。
- Open Policy Agent (OPA,开放策略代理)–作为辅助工具,它通过对外公布HTTP端点,以实现与授权容器的通信。
首先,NGINX会将/authorize的请求发送给授权容器,以授权某个API调用。接着,授权服务会向开放策略代理询问是否有授权请求(true/false)。然后,它向NGINX返回成功(200 OK)或者是失败(403 Forbidden)的响应。据此,NGINX或是允许API的调用,或是向客户端返回403 Forbidden的响应。
什么是开放策略代理?
开放策略代理(OPA)是一个开源的通用策略引擎,它统一了整个栈中的策略执行。OPA提供了一种高级声明性的语言,可方便您将策略转换为代码和简单的API,进而减轻了软件在决策时的负担。您可以在微服务、Kubernetes、CI/CD管道、以及API网关中,使用OPA来实施策略。
由于OPA能够接受JSON之类结构化的数据作为输入,并且可以返回true/false的决策,或将任意结构化的数据作为输出,因此它能够有效地将决策与执行予以脱钩。
值得一提的是,OPA使用rego作为策略语言。您可以通过链接--https://www.openpolicyagent.org/docs/latest/,了解更多有关rego和开放策略代理的信息。
详述授权服务
下面让我们来详细讨论授权服务的具体工作方式。
如上图所示,我们在服务器模式下运行开放策略代理,并利用其REST API的更新策略来获取决策。如下命令展示了开放策略代理通过对外公布REST API,来创建或更新策略。
- PUT /v1/policies/ Content-Type: text/plain
在本例中,我们需要端点接收如下的请求,来更新OPA中的策略(具体可参照它在GitHub里的README)。
- curl -X PUT --data-binary @policies/httpapi.authz.rego http:///authorize/v1/policies/httpapi/authz
同时,我们需要另一个API来根据政策做出决策:
- POST /v1/data/
- Content-Type: application/json
此处的
- {
- "input" : {
- "method": "DELETE",
- "api": "/offer/1000",
- "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NzQ2NjM3MDAsImV4cCI6NDA5OTE4NTMwMCwiYXVkIjoib3BhLWV4YW1wbGUuY29tIiwic3ViIjoianjvy2tldeblegftcgxllmnvbsisikdpdmvutmftzsi6ikpvag5uesisiln1cm5hbwuioijsb2nrzxqilcjfbwfpbci6impyb2nrzxrazxhhbxbszs5jb20iLCJSb2xlIjoiU2FsZXMgQWRtaW4ifQ._UtjZtowF3NNN3IF1t0LBHuzQhdfIfsO8jC-46GvbRM"
- }
- }
由代码可知,授权应用程序将收到从NGINX发来的请求,并根据上面的逻辑图为OPA产生输入请求。针对该示例,我们在前端代码中对JWT进行了硬编码。而且每个API请求都会在Authorization头部包含JWT。据此,授权应用程序在获取JWT后,会将其添加到OPA的输入请求中。其具体Java代码如下:
- Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJMb2NhbEpXVElzc3VlciIsImlhdCI6MTU3MzcyNzM5MSwiZXhwIjo0MDk4MjQ1Mzc4LCJhdWQiOiJvcGEtZXhhbXBsZS5jb20iLCJzdWIiOiJzYWxlc0BleGFtcGxlLmNvbSIsIkdpdmVuTmFtdyI6IkpvaG5ueSIsIlN1cm5hbWUiOiJTYWxlcyIsIkVtYWlsIjoianNhbGVzQGV4YW1wbGUuY29tIiwiUm9sZSI6IlNhbGVzIn0.UbHWQpCMwupzsFp8f0CQ4o_bJSVaBugKijhcURZ_Mko
值得注意的是,授权服务只会从输入的请求中检索JWT,而不会对其进行解码。因此,OPA会通过内置的io.jwt.decode功能函数,去支持JWT的解析。
您可以通过链接--https://play.openpolicyagent.org/p/4LOvGaEXEU,进一步了解rego策略的相关程序代码与逻辑。通过尝试不同的输入请求,您将能够查看到OPA为每一个请求所生成的不同输出。
小结
综上所述,开放策略代理提供了一种将授权决策与微服务中的业务逻辑相分离的方法。在实际应用中,系统管理员可以通过对开放策略代理进行设置,将生成授权策略(rego策略)的责任,委托给各个微服务的所有者。而微服务所有者和系统管理员都不会越界进行任何处理。
原文标题:Open Policy Agent: Microservices Authorization Simplified,作者:Gaurav Chaware
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】