如何在 Google App Engine 和本地开发服务器中使用带有私钥文件和模拟用户 (DWD) 的服务帐户?

How to use service account with private key file and impersonated user (DWD) in Google App Engine and local development server?

应用程序:Google App Engine Python 标准环境

目的:通过 google-api-python-client 访问 Google APIs(不是 Cloud APIs),例如Sheets API v4,通过使用 服务帐户并模拟用户 ,因为该应用程序应该代表该用户执行操作。 (双足认证,不会要求用户授予访问权限)

我在生产环境中设置了 运行,但它仅在本地开发服务器 (dev_appserver.py) 上运行,用于测试是否会删除某个环境变量。我正在寻找一种无需 adding/removing 环境变量即可工作的解决方案。

为应用程序创建了服务帐户,并在管理控制台中配置了全域委派 DWD。工作表 API 已为此项目打开。

在许多可用的快速入门、示例和参考资料中,只有在阅读 Google Auth Library for Python documentation (google-auth) 之后我才注意到缺少的部分(环境变量和SSL 库)并最终在生产环境中获得代码 运行。

应用代码将使用从 Cloud Console IAM 下载的私钥 JSON 文件。

requirements.txt

# as suggested by almost all docs, but this isn't everything we need:
google-api-python-client==1.6.5
google-auth==1.4.0
google-auth-httplib2==0.0.3

app.yaml

env_variables:
  # enable socket support of paid app, needed for OAuth2 service-accounts
  # see google-auth documentation, v1.4.1, chapter 1.2.4
  GAE_USE_SOCKETS_HTTPLIB : true
# some other stuff
libraries:
# to make HTTPS calls to other services, needed for OAuth2 service-accounts
# see google-auth documentation, v1.4.1, chapter 1.2.4
- name: ssl
  version: latest

appengine_config.py(表格 API v4 访问的部分示例)

from google.oauth2 import service_account
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
APP_ROOT_DIR = os.path.abspath(os.path.dirname(__file__))
SERVICE_ACCOUNT_FILE = "service-account-private-key.json"
import googleapiclient.discovery
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
# impersonate as user@example.com (G Suite domain account)
credentials = credentials.with_subject('user@example.com')
service = googleapiclient.discovery.build('sheets', 'v4', credentials=credentials)
# until here, the code works in production and local dev server
result = service.spreadsheets().values().get(spreadsheetId="DOC-ID-HERE", range="A1:C5").execute()
# execute() will work only in production,
# on local dev, it will raise an ResponseNotReady exception

回溯

ERROR    2018-03-05 16:32:03,183 wsgi.py:263] 
Traceback (most recent call last):
  File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 240, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/api/lib_config.py", line 351, in __getattr__
    self._update_configs()
  File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/api/lib_config.py", line 287, in _update_configs
    self._registry.initialize()
  File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/api/lib_config.py", line 160, in initialize
    import_func(self._modname)
  File "/Users/user/git/project/gae/appengine_config.py", line 143, in <module>
    spreadsheetId=spreadsheetId, range=rangeName).execute()
  File "/Users/user/git/project/gae/_lib/oauth2client/_helpers.py", line 133, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/Users/user/git/project/gae/_lib/googleapiclient/http.py", line 839, in execute
    method=str(self.method), body=self.body, headers=self.headers)
  File "/Users/user/git/project/gae/_lib/googleapiclient/http.py", line 166, in _retry_request
    resp, content = http.request(uri, method, *args, **kwargs)
  File "/Users/user/git/project/gae/_lib/google_auth_httplib2.py", line 187, in request
    self._request, method, uri, request_headers)
  File "/Users/user/git/project/gae/_lib/google/auth/credentials.py", line 121, in before_request
    self.refresh(request)
  File "/Users/user/git/project/gae/_lib/google/oauth2/service_account.py", line 322, in refresh
    request, self._token_uri, assertion)
  File "/Users/user/git/project/gae/_lib/google/oauth2/_client.py", line 145, in jwt_grant
    response_data = _token_endpoint_request(request, token_uri, body)
  File "/Users/user/git/project/gae/_lib/google/oauth2/_client.py", line 106, in _token_endpoint_request
    method='POST', url=token_uri, headers=headers, body=body)
  File "/Users/user/git/project/gae/_lib/google_auth_httplib2.py", line 116, in __call__
    url, method=method, body=body, headers=headers, **kwargs)
  File "/Users/user/git/project/gae/_lib/httplib2/__init__.py", line 1659, in request
    (response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
  File "/Users/user/git/project/gae/_lib/httplib2/__init__.py", line 1399, in _request
    (response, content) = self._conn_request(conn, request_uri, method, body, headers)
  File "/Users/user/git/project/gae/_lib/httplib2/__init__.py", line 1355, in _conn_request
    response = conn.getresponse()
  File "/Users/user/google-cloud-sdk/platform/google_appengine/google/appengine/dist27/python_std_lib/httplib.py", line 1121, in getresponse
    raise ResponseNotReady()

我发现如果我从 app.yaml 的 env_variables 列表中删除 GAE_USE_SOCKETS_HTTPLIB,该代码将在本地开发服务器上运行(但不再在生产环境中运行)。

我是不是做错了什么?我可以在两种环境中使用相同的代码(可能有一个小开关),而无需手动 adding/removing 来自 app.yaml 的变量吗?

  1. 目的:通过[=39=访问GoogleAPIs(不是云APIs) ],例如工作表 API v4, ....

Here 他们解释说:

Private, broadcast, multicast, and Google IP ranges (except those whitelisted below), are blocked:

  • Google Public DNS: 8.8.8.8, 8.8.4.4, 2001:4860:4860::8888, 2001:4860:4860::8844 port 53
  • Gmail SMTPS: smtp.gmail.com port 465 and 587
  • Gmail POP3S: pop.gmail.com port 995
  • Gmail IMAPS: imap.gmail.com port 993
  1. 我发现如果我从 app.yaml 的 env_variables 列表中删除 GAE_USE_SOCKETS_HTTPLIB,该代码将在本地开发服务器上运行(但在生产了)。

这个解释here:

Using sockets with the development server

You can run and test code using sockets on the development server, without using any special command line parameters.

最后,这个 and the 描述了一个类似的场景。

希望这对你有帮助:-)