GAE因401而刷新(尝试1/2)

时间:2016-11-01 15:00:29

标签: google-app-engine google-cloud-storage google-cloud-platform google-authentication

我使用gcloud app deploy部署应用程序。它运行良好几个小时,然后工作卡在以下信息日志:

03:54:59.748 Refreshing due to a 401 (attempt 1/2)
03:58:44.816 Refreshing due to a 401 (attempt 1/2)
03:58:55.781 Refreshing due to a 401 (attempt 1/2)
03:58:56.317 Refreshing due to a 401 (attempt 1/2)

似乎是从内部serviceName记录的:" appengine.googleapis.com"

部署的应用程序从GCS读取一些文件,并使用google pubsub发布一些计算的指标。我使用谷歌客户端API库,它使用凭据进行身份验证。

    credentials = GoogleCredentials.get_application_default()
    if credentials.create_scoped_required():
        credentials = credentials.create_scoped(['https://www.googleapis.com/auth/devstorage.read_only)
    http = httplib2.Http()
    credentials.authorize(http)
    return discovery.build('storage', 'v1', http=http)

也是pubsub的类似代码段。有没有人遇到类似的问题,其中请求似乎永远停留在显示401状态代码的信息日志中。不确定为什么请求不会超时并导致失败。任何解决方法?

2 个答案:

答案 0 :(得分:0)

401是未经授权的错误,这意味着OAuth承载令牌不再有效,需要使用刷新令牌进行刷新。您解释的时间表与the duration of the bearer tokens匹配。

我会使用cloud idiomatic libraries,或者如果您使用的是GAE标准the appengine libraries directly。这些库本身处理身份验证(包括在令牌过期时刷新令牌),并包含在App Engine中使用的进一步优化。

答案 1 :(得分:-1)

这让我很头疼,但我现在明白了。

在我的应用中,我想扫描电子邮件中的关键字以获取最高的贝宝收据。当然,我不会透露我的全部技巧,但是我提供了超出上下文和用法所必需的更多内容。我有一个个人帐户,我已重命名为paypalEmailAccount@gmail.com,一个api管理员帐户已重命名为office@myBusiness.com,网站已重命名为www.myBusiness.com。我不想冒意外暴露任何漏洞的风险。

class Quickbooks(webapp2.RequestHandler):
    def get(self):
        self.post()
    def post(self):
        ping(self)
        user = users.get_current_user()
        if (levelOneUsers( user ) or levelTwoUsers( user )):
            nameField = self.request.get('pName')
            if nameField == 'e':
                categoryLevel=0;
                service = getUserGmail(self, 'paypalEmailAccount@gmail.com@gmail.com')
                userName = 'paypalEmailAccount@gmail.com'
                if( service ):
                    officeDriveService = getOfficeDrive()
                    topMessage = getTopMessage( service, officeDriveService, 'in:inbox service@paypal.com' )
    (main code then processes topMessage, no need to see any of that.....lol)

def getUserGmail(self, emailAddress):
    if(emailAddress=='office@myBusiness.com'): 
        return getOfficeGmail()
    else:
        creds_query = Creds.query(ancestor=credsCounter_key(DEFAULT_CREDS_NAME)).order(-Creds.number)
        creds_query2 = creds_query.filter(Creds.number==emailAddress)
        creds_items = creds_query2.fetch(1)
        for creds_item in creds_items:
            jsonCreds = creds_item.jsonString
            check = json.loads(jsonCreds)
            token_info = urllib.urlopen("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + check['access_token'])
            token_info_dict = json.loads(token_info.read())
            if( 'expires_in' in token_info_dict and token_info_dict['expires_in'] > 0 ):
                credentials = OAuth2Credentials.from_json(jsonCreds)
                http_auth = credentials.authorize(Http())
                keepTrying = True
                tryCounter = 0
                while keepTrying:
                    try:
                        tryCounter += 1
                        service = build('gmail', 'v1', http=http_auth)
                    except:
                        time.sleep(1)
                        if (tryCounter >= 10):
                            raise
                    else:
                        keepTrying = False
                return service
        flow = OAuth2WebServerFlow(client_id='???????????-??????????????????????????????????.apps.googleusercontent.com',
                               client_secret='?????????????????????',
                               scope='https://mail.google.com/', 
                               redirect_uri='https://www.myBusiness.com.com/auth_return',
                               login_hint=emailAddress)
        flow.params['access_type'] = 'offline'
        auth_uri = flow.step1_get_authorize_url()
        self.redirect(auth_uri)

class Auth_Return(webapp2.RequestHandler):
    def get(self):
        self.post()
    def post(self):
        ping(self)
        code = nameField = self.request.get('code')
        flow = OAuth2WebServerFlow(client_id='?????????????-?????????????????????????.apps.googleusercontent.com',
                           client_secret='??????????????????',
                           scope='https://mail.google.com/', 
                           redirect_uri='https://www.myBusiness.com.com/auth_return')
        flow.params['access_type'] = 'offline'
        credentials = flow.step2_exchange(code)
        jsonCred = credentials.to_json()
        http_auth = credentials.authorize(Http())
        keepTrying = True
        tryCounter = 0
        while keepTrying:
            try:
                tryCounter += 1
                service = build('gmail', 'v1', http=http_auth)
            except:
                time.sleep(1)
                if (tryCounter >= 10):
                    raise
            else:
                keepTrying = False
        response = service.users().getProfile(userId="me").execute()
        creds_query = Creds.query(ancestor=credsCounter_key(DEFAULT_CREDS_NAME)).order(-Creds.number)
        creds_query2 = creds_query.filter(Creds.number==response['emailAddress'])
        creds_items = creds_query2.fetch(1)
        if(len(creds_items) == 0):
            creds_item = Creds(parent=credsCounter_key(DEFAULT_CREDS_NAME))
            creds_item.number = response['emailAddress']
            creds_item.jsonString = jsonCred
            creds_item.put()
        else:
            for creds_item in creds_items:
                creds_item.jsonString = jsonCred
                creds_item.put()
        self.response.write('<b name="handshake_complete">A security handshake between server and email address: ' + response['emailAddress'] +  ' was required to continue. This is usually due to an expired certificate. This caused the previous request to be lost but the system will redirect back in 1 second...please wait.... A secure connection has been established.</b><br>' + """
<script>
window.location = "https://www.myBusiness.com/quickbooks?pName=????????";
</script>
""")

app = webapp2.WSGIApplication([
    ('/', MainPageBlank ),
    ('/quickbooks', Quickbooks),
    ('/auth_return', Auth_Return),
    .......
], debug=True)

好的....虽然很多,但只有一行需要检查....其余的是您娱乐的上下文。而且不要在任何事情上批评我...不会听到....我是最伟大的

if( 'expires_in' in token_info_dict and token_info_dict['expires_in'] > 0

token_info_dict ['expires_in'](“刷新”)为3600(1小时)时,以秒为单位 每个:Microsoft's Manage access tokens for API requests

嗯,这是一个问题。 Google API线程实例限制为60秒。如果token_info_dict ['expires_in']小于60但大于0,则在处理该60秒间隔内的请求时,代码可能会失败。另外,该错误最多有1/60的几率发生,因此很难确定。

解决方案:

if( 'expires_in' in token_info_dict and token_info_dict['expires_in'] > 60

现在令牌不会在Google api线程时间限制之前过期