Documentation

19. 基于令牌的验证

使用 OAuth 2 进行基于令牌的身份验证。您可以管理 OAuth 令牌以及应用,即用于生成令牌的 API 客户端的服务器端系统。通过将 OAuth 令牌作为 HTTP 验证标头的一部分,您可以自行验证身份,并调整除了基本 RBAC 权限外的限制性权限的程度。如需 OAuth 2 规格的详情,请参阅 RFC 6749

如需有关使用 manage 实用程序创建令牌的详情,请参考 令牌和会话管理 部分。

19.1. 管理 OAuth 2 应用和令牌

应用和令牌可以作为 /api/<version>/applications/api/<version>/tokens 的顶层资源进行管理。这些资源也可以由 /api/<version>/users/N/<resource> 的用户相应地访问。可以通过对 api/<version>/applications/api/<version>/users/N/applications 发出 POST 来创建应用。

每个 OAuth 2 应用都代表了服务器端的一个特定的 API 客户端。对于 API 客户端,要通过应用令牌使用 API,它必须首先具有一个应用并签发了一个访问令牌。每个应用都可通过主键:/api/<version>/applications/<pk>/ 访问。这里是一个典型的应用:

    {
    "id": 1,
    "type": "o_auth2_application",
    "url": "/api/v2/applications/2/",
    "related": {
        "tokens": "/api/v2/applications/2/tokens/"
    },
    "summary_fields": {
        "organization": {
            "id": 1,
            "name": "Default",
            "description": ""
        },
        "user_capabilities": {
            "edit": true,
            "delete": true
        },
        "tokens": {
            "count": 0,
            "results": []
        }
    },
    "created": "2018-07-02T21:16:45.824400Z",
    "modified": "2018-07-02T21:16:45.824514Z",
    "name": "My Application",
    "description": "",
    "client_id": "Ecmc6RjjhKUOWJzDYEP8TZ35P3dvsKt0AKdIjgHV",
    "client_secret": "7Ft7ym8MpE54yWGUNvxxg6KqGwPFsyhYn9QQfYHlgBxai74Qp1GE4zsvJduOfSFkTfWFnPzYpxqcRsy1KacD0HH0vOAQUDJDCidByMiUIH4YQKtGFM1zE1dACYbpN44E",
    "client_type": "confidential",
    "redirect_uris": "",
    "authorization_grant_type": "password",
    "skip_authorization": false,
    "organization": 1
}

如上例所示,name 是应用的人类可读标识符。其它字段(如 client_idredirect_uris)主要用于 OAuth2 授权,它在后面的:ref:ag_use_oauth_pat 中介绍。

client_idclient_secret 字段的值在创建过程中生成 ,它们是应用的不可编辑标识符,而在创建时还需要 organizationauthorization_grant_type,并变为不可编辑。

19.1.1. 应用的访问规则

应用的访问规则如下:

  • 系统管理员可以查看并操作系统中的所有应用

  • 机构管理员可以查看和操作属于机构成员的所有应用

  • 其他用户只能查看、更新和删除自己的应用,但无法创建新应用

另一方面,令牌是用于实际身份验证传入请求和屏蔽底层用户权限的资源。令牌可以通过两个方法创建:

  • 使用 applicationscope 字段发布到 /api/v2/tokens/ 端点,以指向相关应用并指定令牌范围

  • 使用 scope 字段发布到 /api/v2/applications/<pk>/tokens/ 端点(父应用会自动链接)

单独的令牌可以通过它们的主键:/api/<version>/tokens/<pk>/ 访问。这里是一个典型令牌的示例:

{
    "id": 4,
    "type": "o_auth2_access_token",
    "url": "/api/v2/tokens/4/",
    "related": {
        "user": "/api/v2/users/1/",
        "application": "/api/v2/applications/1/",
        "activity_stream": "/api/v2/tokens/4/activity_stream/"
},
    "summary_fields": {
        "application": {
            "id": 1,
            "name": "Default application for root",
            "client_id": "mcU5J5uGQcEQMgAZyr5JUnM3BqBJpgbgL9fLOVch"
        },
        "user": {
            "id": 1,
            "username": "root",
            "first_name": "",
            "last_name": ""
        }
    },
    "created": "2018-02-23T14:39:32.618932Z",
    "modified": "2018-02-23T14:39:32.643626Z",
    "description": "App Token Test",
    "user": 1,
    "token": "*************",
    "refresh_token": "*************",
    "application": 1,
    "expires": "2018-02-24T00:39:32.618279Z",
    "scope": "read"
},

对于 OAuth 2 令牌,唯一完全可编辑的字段是 scopedescriptionapplication 字段在更新时不可编辑,且所有其他字段完全不可编辑,并在创建过程中自动填充,如下所示:

  • user 字段与为其创建令牌的用户对应,在本例中,也是创建令牌的用户

  • expires 根据 Tower 配置设置生成 OAUTH2_PROVIDER

  • tokenrefresh_token 是自动生成的、没有冲突的随机字符串

应用令牌和个人访问令牌都显示在 /api/v2/tokens/ 端点中。个人访问令牌中的 application 字段始终为 null。这是区分两种令牌的好方法。

19.1.2. 令牌的访问规则

令牌的访问规则如下:

  • 如果用户能够查看相关应用,则可以创建令牌;并且用户也可以为自己创建个人令牌。

  • 系统管理员可以查看并操作系统中的每个令牌

  • 机构管理员可以查看和操作属于机构成员的所有令牌

  • 系统审核员(Auditor)可查看所有令牌和应用

  • 其他普通用户只能查看和操作自己的令牌

注解

用户只能在创建时查看令牌或刷新令牌值。

19.2. 将 OAuth 2 令牌系统用于个人访问令牌 (PAT)

获取 OAuth 2 令牌的最简单和最常见的方法是在 /api/v2/users/<userid>/personal_tokens/ 端点上创建个人访问令牌,如下例所示:

curl -XPOST -k -H "Content-type: application/json" -d '{"description":"Personal Tower CLI token", "application":null, "scope":"write"}' https://<USERNAME>:<PASSWORD>@<TOWER_SERVER>/api/v2/users/<USER_ID>/personal_tokens/ | python -m json.tool

如果已安装,您还可以通过 jq 对 JSON 输出进行 pipe 操作。

以下是使用个人令牌访问使用 curl 的 API 端点的示例:

curl -k -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -X POST  -d '{}' https://tower/api/v2/job_templates/5/launch/

在 Ansible Tower 中,OAuth 2 系统构建在 Django Oauth Toolkit 上,为授权、撤销和刷新令牌提供专用端点。这些端点可以在 /api/v2/users/<USER_ID>/personal_tokens/ 端点下找到,它还提供了这些端点的一些用法的详细示例。这些特殊 OAuth 2 端点仅支持使用 x-www-form-urlencoded Content-type,因此没有 api/o/* 端点接受 application/json

注解

您还可以通过为应用程序类型指定 null ,使用 /api/o/token 端点来请求令牌。

或者,您可以通过 Tower 用户界面为用户 add tokens,并配置访问令牌的过期时间及其关联的刷新令牌(如果适用)。

_images/configure-tower-system-misc-sys-token-expire.png

19.2.1. RBAC 系统的令牌范围掩码

OAuth 2 令牌的范围是一个由空格分隔的、包括有效范围关键字(’read’ 和 ’write’)的字符串。这些关键字可以被配置,用来指定经过身份验证的 API 客户端的权限级别。通过使用 read 和 write 范围,可以在 Ansible Tower 的基于角色的访问控制 (RBAC) 权限系统上提供一个掩码层。”write” 范围为经过身份验证的用户提供了 RBAC 系统的完整权限,而“read”范围则仅为经过身份验证的用户提供读取权限。请注意,“write”包括了“read”的权限。

例如,如果您对作业模板有管理权限,则当通过会话或基本身份验证进行了身份验证后,可以查看、修改、启动和删除作业模板。相反,如果您使用 OAuth 2 令牌进行身份验证,而相关的令牌范围为“read”,您只能查看,但不能操作或启动作业模板,即使管理员也是如此。如果令牌范围是“write”或“read write”,则可以作为管理员充分利用作业模板。

要获取和使用令牌,首先请创建一个应用令牌:

  1. 创建一个应用,authorization_grant_type 设置为 password。HTTP 将以下内容 POST 到 /api/v2/applications/ 端点(提供您自己的机构 ID):

{
    "name": "Admin Internal Application",
    "description": "For use by secure services & clients. ",
    "client_type": "confidential",
    "redirect_uris": "",
    "authorization_grant_type": "password",
    "skip_authorization": false,
    "organization": <organization-id>
}
  1. 创建一个端点并 POST 到 /api/v2/tokens/

{
    "description": "My Access Token",
    "application": <application-id>,
    "scope": "write"
}

这会返回 <token-value>,可以用来为将来的请求进行身份验证(这不会再次显示)。

  1. 使用令牌访问资源。以下使用 curl 作为示例:

curl -H "Authorization: Bearer <token-value>" -H "Content-Type: application/json" -X GET https://<tower>/api/v2/users/

如果您还没有设置 CA 且正在使用 SSL,则可能需要 -k 标志。

要撤销令牌,您可以使用该令牌的 ID 在该令牌的详情页面上进行 DELETE。例如:

curl -ku <user>:<password> -X DELETE https://<tower>/api/v2/tokens/<pk>/

同样,使用令牌:

curl -H "Authorization: Bearer <token-value>" -X DELETE https://<tower>/api/v2/tokens/<pk>/ -k

19.3. 应用功能

本页列出了用于授权、令牌刷新和撤销的 OAuth 2 实用程序端点。/api/o/ 端点不会在浏览器中使用,且不支持 HTTP GET。这里介绍的端点严格遵循 OAuth 2 的 RFC 规格,因此使用它进行详细参考。以下示例是 Tower 中这些端点的典型用法,特别是在使用各种授权类型创建应用时:

  • 授权代码

  • Password

注解

您可以使用 Tower 用户界面执行此处所述的全部应用功能。如需更多详情,请参阅 Ansible Tower User GuideApplications 部分。

19.3.1. 使用 authorization code 授权类型的应用

当访问令牌需要签发到一个外部应用程序或服务时,应使用 authorization code 授权类型。

注解

当使用一个应用程序时,只能使用 authorization code 类型来获取访问令牌。如果一个外部 web 应用程序与 Ansible Tower 集成,那个 web 应用程序可能需要为该应用程序上的用户创建 OAuth2 令牌。实现这个目的的首选做法是在 Tower 中创建 authorization code 授权类型的应用程序。这是因为:

  • 外部应用程序可以为一个用户从 Tower 获取令牌作为用户的凭证

  • 对为特定应用程序发布的令牌进行分离,可轻松管理这些令牌(例如,在不需要删除系统中*所有*令牌的情况下,删除与该应用程序关联的所有令牌)

要使用 authorization-code 授权类型创建名为 AuthCodeApp 的应用程序,请对 /api/v2/applications/ 端点执行 POST:

{
    "name": "AuthCodeApp",
    "user": 1,
    "client_type": "confidential",
    "redirect_uris": "http://<tower>/api/v2",
    "authorization_grant_type": "authorization-code",
    "skip_authorization": false
}


.. _`Django-oauth-toolkit simple test application`: http://django-oauth-toolkit.herokuapp.com/consumer/

当使用 response_typeclient_idredirect_urisscope 从客户端应用签发 GETauthorize 端点时发生的工作流:

  1. Tower 使用授权代码和状态响应应用中指定的 redirect_uri

  2. 客户端应用然后使用 codeclient_idclient_secretgrant_typeredirect_uri,向 Tower 上的 api/o/token/ 端点发出 POST

  3. Tower 使用 access_tokentoken_typerefresh_tokenexpires_in 响应。

请参阅 Django's Test Your Authorization Server 工具包来测试这个流。

您可以在 Configure Tower - System 设置中指定授权代码保持有效的秒数:

_images/configure-tower-system-misc-sys-authcode-expire.png

此持续时间后请求访问令牌将会失败。根据 RFC6749 的建议,持续时间默认为 600 秒(10 分钟)。

使用授权代码授权类型设置应用与 Ansible Tower 集成的最佳方法是:将这些跨站点请求的起源列入白名单。更笼统地说,您需要将您要与 Tower 集成的服务或应用列入白名单,以便为其提供访问令牌。为此,您的管理员将这份白名单添加到其本地 Tower 设置中:

CORS_ORIGIN_REGEX_WHITELIST = [
    r"http://django-oauth-toolkit.herokuapp.com*",
    r"http://www.example.com*"
]

其中 http://django-oauth-toolkit.herokuapp.comhttp://www.example.com 是需要令牌才能访问 Tower 的应用。

19.3.2. 使用 password 授权类型的应用

passwordResource owner password-based 授权类型非常适合拥有对 web 应用的原生访问权限并在客户端为资源拥有者时使用的用户。以下是带有授权类型 password 的 'Default Application':

{
    "id": 6,
    "type": "application",
    ...
    "name": "Default Application",
    "user": 1,
    "client_id": "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l",
    "client_secret": "fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569eIaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo",
    "client_type": "confidential",
    "redirect_uris": "",
    "authorization_grant_type": "password",
    "skip_authorization": false
}

password 授权类型不需要登录,因此您可以简单地使用 curl 通过 /api/v2/tokens/ 端点获取个人访问令牌:

curl -k --user <user>:<password> -H "Content-type: application/json" \
-X POST \
--data '{
    "description": "Token for Nagios Monitoring app",
    "application": 1,
    "scope": "write"
}' \
https://<tower>/api/v2/tokens/

注解

特殊 OAuth 2 端点只支持使用 x-www-form-urlencoded Content-type,因此,没有 api/o/* 端点接受 application/json

成功后,会以 JSON 格式显示一个响应,其中包含访问令牌、刷新令牌和其他信息:

HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 05 Dec 2017 16:48:09 GMT
Content-Type: application/json
Content-Length: 163
Connection: keep-alive
Content-Language: en
Vary: Accept-Language, Cookie
Pragma: no-cache
Cache-Control: no-store
Strict-Transport-Security: max-age=15768000

{"access_token": "9epHOqHhnXUcgYK8QanOmUQPSgX92g", "token_type": "Bearer", "expires_in": 315360000000, "refresh_token": "jMRX6QvzOTf046KHee3TU5mT3nyXsz", "scope": "read"}

19.4. 应用令牌功能

本节介绍了与令牌关联的刷新和撤销功能。所有随后的功能(刷新和撤销 /api/o/ 端点中的令牌)目前只能使用应用令牌完成。

19.4.1. 刷新现有访问令牌

以下示例显示了提供有刷新令牌的现有访问令牌:

{
    "id": 35,
    "type": "access_token",
    ...
    "user": 1,
    "token": "omMFLk7UKpB36WN2Qma9H3gbwEBSOc",
    "refresh_token": "AL0NK9TTpv0qp54dGbC4VUZtsZ9r8z",
    "application": 6,
    "expires": "2017-12-06T03:46:17.087022Z",
    "scope": "read write"
}

/api/o/token/ 端点用于刷新访问令牌:

curl -X POST \
    -d "grant_type=refresh_token&refresh_token=AL0NK9TTpv0qp54dGbC4VUZtsZ9r8z" \
    -u "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l:fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569eIaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo" \
    http://<tower>/api/o/token/ -i

在上述 POST 请求中,refresh_token 由上方的访问令牌的 refresh_token 字段提供。身份验证信息的格式是 <client_id>:<client_secret>,其中 client_idclient_secret 是与访问令牌的底层相关应用的对应字段。

注解

特殊 OAuth 2 端点只支持使用 x-www-form-urlencoded Content-type,因此,没有 api/o/* 端点接受 application/json

成功后,会以 JSON 格式显示一个响应,其中包含新的(刷新的)访问令牌,其范围信息与前面的信息相同:

HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 05 Dec 2017 17:54:06 GMT
Content-Type: application/json
Content-Length: 169
Connection: keep-alive
Content-Language: en
Vary: Accept-Language, Cookie
Pragma: no-cache
Cache-Control: no-store
Strict-Transport-Security: max-age=15768000

{"access_token": "NDInWxGJI4iZgqpsreujjbvzCfJqgR", "token_type": "Bearer", "expires_in": 315360000000, "refresh_token": "DqOrmz8bx3srlHkZNKmDpqA86bnQkT", "scope": "read write"}

本质上,刷新操作通过删除原始令牌替换现有的令牌,然后立即创建一个新的令牌,其范围和相关应用与原始令牌相同。验证是否存在新令牌,并在 /api/v2/tokens/ 端点中删除旧令牌。

19.4.2. 撤销访问令牌

同样,您可以使用 /api/o/revoke-token/ 端点来撤销访问令牌。

通过此方法撤销访问令牌与删除令牌资源对象相同,但它允许您通过提供令牌值以及关联的 client_id``(如果应用程序是 ``confidential,则为 client_secret)来删除令牌。例如:

curl -X POST -d "token=rQONsve372fQwuc2pn76k3IHDCYpi7" \
-u "gwSPoasWSdNkMDtBN3Hu2WYQpPWCO9SwUEsKK22l:fI6ZpfocHYBGfm1tP92r0yIgCyfRdDQt0Tos9L8a4fNsJjQQMwp9569eIaUBsaVDgt2eiwOGe0bg5m5vCSstClZmtdy359RVx2rQK5YlIWyPlrolpt2LEpVeKXWaiybo" \
http://<tower>/api/o/revoke_token/ -i

注解

特殊 OAuth 2 端点只支持使用 x-www-form-urlencoded Content-type,因此,没有 api/o/* 端点接受 application/json

注解

**Allow External Users to Create Oauth2 Tokens**(API 中的 ALLOW_OAUTH2_FOR_EXTERNAL_USERS)设置默认禁用。外部用户是指使用类似 LDAP 的服务或其它 SSO 服务进行外部身份验证的用户。此设置确保外部用户无法*创建*自己的令牌。如果启用了这个设置并在随后禁用它,则外部用户在此期间创建的令牌仍会存在,它们不会被自动撤销。

或者,您可以使用 manage 实用程序 revoke_oauth2_tokens 撤销令牌,如 令牌和会话管理 部分中所述。

此设置可以在 Ansible Tower 用户界面中的系统级别进行配置:

_images/configure-tower-system-oauth2-tokens-toggle.png

成功后,会显示一个响应 200 OK。通过检查 /api/v2/tokens/ 端点中是否存在令牌来验证删除。