1

我正在使用 Python 中的Google Contacts APIGoogle Calendar API。前者是 GData API,后者是 Google API...API,因此虽然客户端可用,但它们每个都由单独的客户端覆盖——这里是GDataGoogle API

我与这些客户合作时遇到的问题是他们都有自己处理 OAuth2 的方式。GData 库提供了将身份验证令牌gdata.gauth.token_to_blob(auth_token)gdata.gauth.token_from_blob(auth_token)字符串转换为/从字符串转换以存储在数据库中的方法,而 google-api 库在 App Engine(我正在编写的平台)上提供了一种方法来存储 OAuth 凭据。

我没有看到一种清晰的方法来存储两个 API 都可以访问的单个事物(无论是访问令牌还是凭据),但我真的不希望用户必须进行两次身份验证。除了放弃 Google 的客户端库并编写直接的 HTTP 调用之外,有没有办法做到这一点?

4

2 回答 2

3

我能够进行以下工作。它使用oauth2decorator来完成繁重的工作,然后使用一个小的帮助程序类TokenFromOAuth2Creds将这些相同的凭据应用到 gdata 客户端。

我应该先声明我不是 gdata 专家——可能有更好的方法来做到这一点——而且我还没有彻底测试过。

import webapp2
import httplib2
from oauth2client.appengine import oauth2decorator_from_clientsecrets
from apiclient.discovery import build

import gdata.contacts.client

decorator = oauth2decorator_from_clientsecrets(
  "client_secrets.json",
  scope=["https://www.google.com/m8/feeds", "https://www.googleapis.com/auth/calendar.readonly"]
)


# Helper class to add headers to gdata
class TokenFromOAuth2Creds:
  def __init__(self, creds):
    self.creds = creds
  def modify_request(self, req):
    if self.creds.access_token_expired or not self.creds.access_token:
      self.creds.refresh(httplib2.Http())
    self.creds.apply(req.headers)


class MainHandler(webapp2.RequestHandler):
  @decorator.oauth_required
  def get(self):
    # This object is all we need for google-api-python-client access
    http = decorator.http()

    # Create a gdata client
    gd_client = gdata.contacts.client.ContactsClient(source='<var>YOUR_APPLICATION_NAME</var>')

    # And tell it to use the same credentials
    gd_client.auth_token = TokenFromOAuth2Creds(decorator.get_credentials())

    # Use Contacts API with gdata library
    feed = gd_client.GetContacts()
    for i, entry in enumerate(feed.entry):
      self.response.write('\n%s %s' % (i+1, entry.name.full_name.text if entry.name else ''))

    # Use Calendar API with google-api-python-client
    service = build("calendar", "v3")
    result = service.calendarList().list().execute(http=http)
    self.response.write(repr(result))

app = webapp2.WSGIApplication([
  ("/", MainHandler),
  (decorator.callback_path, decorator.callback_handler()),
], debug=True)

请注意,如果您没有使用装饰器,并且通过其他方式获取了您的凭据对象,您可以通过以下方式创建相同的预授权 http 对象:

http = credentials.authorize(httplib2.Http())

使用 gdata 的另一种方法是直接使用http对象(由 返回decorator.http())-该对象将自动为您添加正确的授权标头-这可用于向任一 API 发出请求,但您需要处理制作请求和自己解析 XML/JSON:

class MainHandler(webapp2.RequestHandler):
  @decorator.oauth_required
  def get(self):
    http = decorator.http()

    self.response.write(http.request('https://www.google.com/m8/feeds/contacts/default/full')[1])    
    self.response.write(http.request('https://www.googleapis.com/calendar/v3/users/me/calendarList')[1])

httplib2 的文档:http: //httplib2.googlecode.com/hg/doc/html/libhttplib2.html#http-objects

于 2013-11-15T22:36:48.207 回答
0

看来您需要破解自己的方式并重写 GData API 以让您使用来自Storage的令牌。

但是,即使您能够让 GData 表现得如您所愿,请记住令牌是为特定范围提供的,因此您需要强制 GData API 使用 Google Calendar API,反之亦然。不过不知道能不能实现。

于 2013-11-15T13:37:11.560 回答