使用 OAuth2 范围

本页面适用于 ApigeeApigee Hybrid

查看 Apigee Edge 文档。

本主题介绍如何通过 Apigee 使用和强制执行 OAuth 2.0 范围。

什么是 OAuth2 范围?

OAuth 2.0 范围提供了一种限制访问令牌授权访问数量的方法。例如,可以向客户端应用授予的访问令牌授予对受保护资源的读取和写入权限,也可以仅授予读取权限。您可以实现您的 API,以强制执行您希望的任何范围或组合。因此,如果客户端收到具有读取范围的令牌,且它尝试调用需要写入权限的 API 端点,则调用将会失败。

在本主题中,我们将讨论如何将范围分配给访问令牌,以及 Apigee 如何执行 OAuth 2.0 范围。阅读本主题后,您可以放心地使用范围。

分配给访问令牌的范围如何?

当 Apigee 生成访问令牌时,它可能会为该令牌分配一个范围。要了解发生这种情况的原因,您必须先熟悉以下 Apigee 实体:API 产品、开发者和开发者应用。如需了解介绍,请参阅发布简介。若您需要,我们建议您先查阅此材料,然后再继续操作。

访问令牌是由随机字符组成的长字符串,允许 Apigee 验证传入的 API 请求(将其视为典型用户名/密码凭据的替代)。从技术上讲,令牌是指代如下所示元数据集的密钥:

{
  "issued_at" : "1416962591727",
  "application_name" : "0d3e1d41-a59f-4d74-957e-d4e3275d4781",
  "scope" : "A",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-bs0cSuqS9y]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-AdBmANhsag@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "eTtB7w5lvk3DnOZNGReBlvGvIAeAywun",
  "access_token" : "ODm47ris5AlEty8TDc1itwYPe5MW",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

令牌的元数据包含实际的访问令牌字符串、到期信息、开发者应用的标识、开发者以及与该令牌相关的产品。您还会发现,元数据还包含“范围”。

令牌如何获取其范围?

了解范围的首要是记住,开发者应用中的每个产品都可能被分配零个或多个范围。可以在产品创建时分配这些范围,也可以稍后添加。它们以名称列表形式存在,包含在与每个产品相关的“元数据”中。

当您创建开发者应用并向其中添加产品时,Apigee 会查看开发者应用中的所有产品,并创建那些产品所有范围的清单(应用的主列表或全局范围列表 -- 所有识别的范围集合)。

当客户端应用从 Apigee 请求访问令牌时,它可以选择指定想要与该令牌关联的范围。例如,以下请求指定的是范围“A”。也就是说,客户端要求授权服务器 (Apigee) 生成具有范围“A”的访问令牌(向应用授权调用具有范围“A”的 API)。该应用发送如下 POST 请求:

curl -i -X POST -H Authorization: Basic Mg12YTk2UkEIyIBCrtro1QpIG \
  -H content-type:application/x-www-form-urlencoded \
  https://apitest.acme.com/oauth/token?grant_type=client_credentials&scope=A

具体变化?

当 Apigee 收到此请求时,它会知道哪个应用正在发出请求,并且知道客户端注册了哪个开发者应用(客户端 ID 和客户端密钥在基本 Auth 标头中进行了编码)。由于包含 scope 查询参数,因此 Apigee 需要确定与开发者应用关联的任何 API 产品是否具有“A”范围。如果这样做,则会生成一个范围为“A”的访问令牌。查看此问题的另一种方式:范围查询参数是一种过滤条件。如果开发者应用识别范围“A, B, X”,并且查询参数指定“scope=X Y Z”,则系统仅会将令牌“X”分配给令牌。

如果客户端未附加范围参数会怎样?在这种情况下,Apigee 会生成一个令牌,其中包含开发者应用识别的所有范围。请务必注意,默认行为是返回访问令牌(其中包含开发者应用中包含的所有产品的所有范围的集合)。

如果与开发者应用关联的任何产品都不指定范围,并且令牌确实有范围,则使用该令牌进行的调用将失败。

假设某个开发者应用可识别以下范围:A B C D。这是应用的范围主列表。可能是应用中的一款产品是范围 A 和 B,第二个产品的范围是 C 和 D,也可能是任何组合。如果客户端未指定 scope 参数(或如果参数指定了没有值的范围参数),则令牌将获得全部 4 个范围:A、B、C 和 D。同样,令牌会收到一组范围,这些范围是开发者应用识别的所有范围的集合。

还有一种情况,即默认行为是返回具有所有已识别范围的访问令牌,并且 GenerateAccessToken 政策(生成访问令牌的 Apigee 政策)指定 <Scope> 元素。例如,下面是一个已指定 <Scope> 的 GenerateAccessToken 政策。如果缺少 <Scope> 元素(或该元素存在但为空),则会执行默认行为。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-GenerateAccessToken">
    <DisplayName>OAuthV2 - Generate Access Token</DisplayName>
    <Attributes>
      <Attribute name='hello' ref='system.time' display='false'>value1</Attribute>
    </Attributes>
    <Scope>request.queryparam.scope</Scope> 
    <GrantType>request.formparam.grant_type</GrantType>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>GenerateAccessToken</Operation>
    <SupportedGrantTypes>
      <GrantType>client_credentials</GrantType>
    </SupportedGrantTypes>
  <GenerateResponse enabled="true"/>
</OAuthV2>

范围是如何强制执行的?

首先,请记住,在 Apigee 上,访问令牌已使用 OAuthV2 政策进行验证(通常置于代理流程的最开始)。政策必须指定 VerifyAccessToken 操作。让我们来了解一下此政策:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope> <!-- Optional: space-separated list of scope names. -->
    <GenerateResponse enabled="true"/>
</OAuthV2>

请注意 <Scope> 元素。用于指定此政策将接受哪些范围。

在此示例中,仅当访问令牌包含范围“A”时,此政策才会成功。如果忽略此 <Scope> 元素或没有任何值,则政策会忽略访问令牌的范围。

现在,您可以基于范围验证访问令牌,您可以设计 API 以强制执行特定范围。为此,您可以设计自定义流,并附加具有已知范围的 VerifyAccessToken 政策。

假设您的 API 为端点 /resourceA 定义了一个流:

<Flow name="resourceA">
            <Condition>(proxy.pathsuffix MatchesPath "/resourceA") and (request.verb = "GET")</Condition>
            <Description>Get a resource A</Description>
            <Request>
                <Step>
                    <Name>OAuthV2-VerifyAccessTokenA</Name>
                </Step>
            </Request>
            <Response>
                <Step>
                    <Name>AssignMessage-CreateResponse</Name>
                </Step>
            </Response>
        </Flow>

当此流被触发(请求路径后缀为 /resourceA)时,会立即调用 OAuthV2-VerifyAccessTokenA 政策。此政策用于验证访问令牌是否有效,并查看令牌支持的范围。如果此政策配置为以下示例 <Scope>A</Scope>,则只有在访问令牌的范围为“A”时,此政策才会成功。否则,将返回错误。

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

总而言之,API 开发者负责在其 API 中设计范围强制执行。为此,需创建自定义流来处理特定范围,并附加 VerifyAccessToken 政策以强制执行这些范围。

代码示例

最后,让我们来看一些 API 调用示例,以帮助说明令牌如何接收范围以及如何强制执行范围。

默认案例

假设您有一个包含产品的开发者应用,并且这些产品的范围是:A、B 和 C。此 API 调用请求访问令牌,但不指定范围查询参数。

curl -X POST -H content-type:application/x-www-form-urlencoded \
  https://apitest.acme.com/scopecheck1/token?grant_type=client_credentials

在这种情况下,生成的令牌将获得 A、B 和 C 等范围(默认行为)。令牌的元数据如下所示:

{
  "issued_at" : "1417016208588",
  "application_name" : "eb1a0333-5775-4116-9eb2-c36075ddc360",
  "scope" : "A B C",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-yEgQbQqjRR]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-yxiuHuZcDW@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "atGFvl3jgA0pJd05rXKHeNAC69naDmpW",
  "access_token" : "MveXpj4UYXol38thNoJYIa8fBGlI",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

现在,假设您有一个 API 端点,其范围为“A”,也就是说,VerifyAccessToken 需要范围“A”。下面是 VerifyAccessToken 政策:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

以下是调用和强制执行范围 A 端点的示例:

curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI \
  https://apitest.acme.com/scopecheck1/resourceA

此 GET 调用成功:

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }

此操作之所以有效,是因为调用端点时会触发 VerifyAccessToken 政策,需要范围是 A,访问令牌授权范围是 A、B 和 C(默认行为)。

过滤案例

假设您具有产品的开发者应用范围为 A、B、C 和 X。您可以请求访问令牌并添加 scope 查询参数,具体如下:

curl -i -X POST -H content-type:application/x-www-form-urlencoded \
  'https://apitest.acme.com/oauth/token?grant_type=client_credentials&scope=A X'

在这种情况下,系统将为生成的令牌赋予范围 A 和 X,因为 A 和 X 都是有效范围。请注意,开发者应用可识别 A、B、C 和 X 范围。在这种情况下,您将根据这些范围过滤 API 产品列表。如果产品具有范围 A 或 X,您可以配置将强制执行这些范围的 API 端点。如果产品没有范围 A 或 X(例如,它在 B、C 和 Z 范围内),则无法通过此令牌调用强制执行范围 A 或 X 的 API。

当您使用新令牌调用 API 时:

curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG \
  https://apitest.acme.com/scopecheck1/resourceX

访问令牌由 API 代理验证。例如:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenX">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A X</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

GET 调用成功触发,并返回响应。例如:

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }

它成功的原因是 VerifyAccessToken 政策要求范围 A 或 X,且访问令牌包含范围 A 和 X。当然,如果将 <Scope> 元素设置为“B”,则此调用将失败。

总结

请务必了解 Apigee 如何处理 OAuth 2.0 范围。以下是要点:

  • 开发者应用“识别”为其所有产品定义的所有范围集合。
  • 应用在请求访问令牌时,有机会指定其想要使用的范围。根据请求的范围 (a) 和开发者应用识别的范围 (b),由 Apigee(授权服务器)自行确定实际分配给访问令牌的范围。
  • 如果 Apigee 未配置为检查范围(VerifyAccessToken 政策中缺少 <Scope> 元素或该元素为空),那么只要访问权限中嵌入的范围与开发者应用识别的其中一个范围相匹配,API 调用就会成功(应用的范围主列表中的一个范围)。
  • 如果访问令牌没有任何关联的范围,那么只有在 Apigee 不考虑范围时(VerifyAccessToken 政策中缺少 <Scope> 元素或为空),它才会成功。