1

设想:

授予访问权限时,我将令牌保存在每个应用程序用户的数据存储中。

我需要创建几个 GData 联系人授权客户端以访问不同用户的联系人,我从数据存储区获取所需用户的令牌并创建授权联系人客户端,例如将一个用户的联系人复制到其他用户的联系人。

问题:

授权客户端按预期工作并获取使用其令牌的用户的联系人提要,但使用令牌,令牌到期日期也被保存,它在一小时或 45 分钟内到期,然后它不再工作,我读到如果刷新令牌存在于令牌对象中,如果短期访问令牌已过期,则令牌将自动获取新的访问令牌,但这没有发生。

问题:

如果令牌已过期,我如何在从数据存储中获取令牌后刷新它,以便只要用户不撤销访问权限,我就可以访问用户的联系人?

代码 :

SCOPE = 'https://www.google.com/m8/feeds'
REDIRECT_URI = 'http://localhost:8888/mainPage'
USER_AGENT = 'shared-contacts'

token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID,
                              client_secret=CLIENT_SECRET,
                              scope=SCOPE,
                              user_agent=USER_AGENT)

class MyEntity(db.Model):
  userEmail = db.StringProperty()
    access_token = ObjectProperty()

class ObjectProperty(db.BlobProperty):
    def validate(self, value):
        try:
            result = pickle.dumps(value)
            return value
        except pickle.PicklingError, e:
            return super(ObjectProperty, self).validate(value)

    def get_value_for_datastore(self, model_instance):
        result = super(ObjectProperty, self).get_value_for_datastore(model_instance)
        result = pickle.dumps(result)
        return db.Blob(result)

    def make_value_from_datastore(self, value):
        try:
            value = pickle.loads(str(value))
        except:
            pass
        return super(ObjectProperty, self).make_value_from_datastore(value)

class MainHandler(webapp2.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if user:
            self.redirect(token.generate_authorize_url(
                                                  redirect_uri=REDIRECT_URI,
                                                  access_type='offline')
                                                  )
        else:
            self.redirect(users.create_login_url("/"))

class MainPage(webapp2.RequestHandler):

    def get(self):
        # Getting the token from datastore, if token exists for this user then
      # proceed otherwise get the access token and save in datastore.

      accessToken = Fetch_Access_Token()

        if accessToken:
            pass
        else:
            url = atom.http_core.Uri.parse_uri(self.request.uri)
            code = url.query
            code = code['code']

            if 'error' in code:
                # User Did Not Grant The Access
                pass
            else:
                token_user_email = users.get_current_user().email()
                token.GetAccessToken(code)
                entity = MyEntity(userEmail=token_user_email, access_token=token)
                entity.put()

        self.response.out.write(template.render('index.html',{}))

class RPCMethods():
    def MoveContacts(self, *args):

      # All tokens except current user
      shared_group_users = shared_users(skip_current_user)

        contact_client = gdata.contacts.client.ContactsClient(source=USER_AGENT)

      for sg_user in shared_group_users.itervalues():

          shared_user_token = sg_user[1]

          # PROBLEM OCCURS HERE, IT WORKS FOR ONLY 1 HOUR AND HERE I NEED
          TO REFRESH THIS TOKEN.

          # For almost an hour i get other user's Data, but after that i
          get current user's Data.

          shared_user_authorized_client = shared_user_token.authorize(contact_client)
          shared_group_contact_feed = shared_user_authorized_client.GetContacts()

application = webapp2.WSGIApplication([
                                      ('/', MainHandler),
                                      ('/mainPage', MainPage),
                                      ('/rpc', RPCHandler),
                                      ], debug=True)

更新:

工作代码:

shared_user_credentials = StorageByKeyName(CredentialsModel, shared_user_credentials_key, 'credentials').get()

    if shared_user_credentials.access_token_expired == True:
        http = httplib2.Http()
        http = shared_user_credentials.authorize(http)
        shared_user_credentials.refresh(http)
4

1 回答 1

2

所以 OAuth 2.0 代码“完全没问题”,但是您在后续需要它时获取令牌的方式是问题所在。

在另一篇文章中,我提到了 OAuth2.0 文档的离线访问部分:

当您的应用程序收到刷新令牌时,存储该刷新令牌以供将来使用非常重要。如果您的应用程序丢失了刷新令牌,则必须重新提示用户同意,然后才能获取另一个刷新令牌。如需重新提示用户同意,请approval_prompt在授权码请求中包含该参数,并将值设置为force

简单修复:

所以当你打电话时generate_authorize_url,发送approval_prompt='force'

self.redirect(token.generate_authorize_url(redirect_uri=REDIRECT_URI,
                                           access_type='offline',
                                           approval_prompt='force')

您会注意到它approval_prompt已经在该方法的签名generate_authorize_url

实际修复:

当您存储令牌时:

token_user_email = users.get_current_user().email()
token.GetAccessToken(code)
entity = MyEntity(userEmail=token_user_email, access_token=token)

您知道您将为当前用户设置一个实体。我建议将其存储为

entity = MyEntity(id=token_user_email, access_token=token).

当您让您的用户通过 OAuth 2.0 流程时,

user = users.get_current_user()
if user:
    self.redirect(token.generate_authorize_url(
    ...

您应该首先检查给定用户是否已有令牌:

token = MyEntity.get_by_id(user.email())
if token is not None:
    # do something

你应该真的,真的真的,不要token用作全局对象!

老实说,有很多方法可以解决这个问题,你最好使用OAuth2Decoratorfrom google-api-python-client

有关如何将此装饰器与 结合的一些参考gdata-python-client,请查看我在另一个问题上发布的答案。

离别说明:

您应该使用ndb 数据存储库。不必编写那个可怕的ObjectProperty,您可以简单地使用ndb.PickleProperty而无需您付出任何努力。

于 2013-02-12T18:10:29.787 回答