幸运的是,有一个简单的解决方法.现在,您看到 401 错误这一事实意味着您的数据存储区以及可能还有 Memcache 中存储了格式错误的粘性凭证.导航到您的 appengine.google.com 页面,假设您尚未在那里提供实时生产关键型 Web 应用程序,请转到左侧的 Datastore Viewer,查看对于 Query ->按种类: 下拉菜单,找到 StoredCredential,检查所有项目,假设它们可能都来自您的日历示例,然后单击删除".同时导航到左侧菜单上的 Memcache Viewer,然后单击 Flush Cache.现在,后端试图返回缺少 refreshToken 的凭据的原因似乎是由于客户端试图错误地使用自动批准".事实证明,当我使用新的 JSON client_secrets 创建一个全新的客户端 ID,然后在第一次加载示例日历应用程序时,我发现我的日志记录语句位于src/main/java/com/google/api/services/samples/calendar/appengine/server/Utils.java 文件:Credential credential = newFlow().loadCredential(userId);如果 (credential.getRefreshToken() != null) {logger.log(Level.SEVERE, "刷新令牌不为空");} 别的 {logger.log(Level.SEVERE, "刷新令牌为空!");}然后我会为我尝试访问它的每个唯一登录用户名获得一个 refreshToken.但是,如果我从 Datastore 和 Memcache 中清除凭据,强制对后续请求进行重新身份验证,我将不再看到批准提示,我的凭据也不再具有 refreshTokens,从而导致它们在 1 小时后停止工作.解决方案(tl;dr)在为 StoredCredential 类型的所有实体清除数据存储区并刷新 Memcache 后,只需将 .setApprovalPrompt("force") 添加到 .setApprovalPrompt("force") 到 .setApprovalPrompt("force")代码>src/main/java/com/google/api/services/samples/calendar/appengine/server/Utils.java;该方法将如下所示:静态 GoogleAuthorizationCodeFlow newFlow() 抛出 IOException {返回新的 GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(DATA_STORE_FACTORY).setAccessType("offline").setApprovalPrompt("force").build();}可选地,在获取凭证的位置添加一些日志记录(记录实际的 accessToken 尤其是 refreshToken 通常是不好的做法,但在调试期间执行一两次可能不会造成伤害:),然后重新更新您的应用.您会发现第一次访问您的应用时,您现在会收到批准提示,然后它应该可以安全地永远工作,因为 refreshToken 永远不会过期并且现在保存在您的数据存储中.请注意,曾经发布的每个客户端/用户 ID 对的 refreshToken 数量是有限制的,如果您在调试期间清除了数据存储/内存缓存,它会有效地泄漏 refreshToken.行为只是在 25 个这样的令牌之后,较早的令牌将自动停用.有关该限制的更多信息,请参阅 oauth2 文档.I am new to using Google APIs. I followed steps to setup Google Calendar sample code in eclipse using Google client library. I deployed the code to app engine using the cmd command- mvn appengine:update (when tried to deploy through eclipse it gave me an error saying the project is not an App Engine project). Deploying through command line worked and I could launch my web application.Suddenly, after few API request calls, started getting invalid credentials error:I searched this error and found it may occur if your auth token is invalid or expired. I am not sure what I need to do to get a new auth token. In my case, there is a client_secrets.json file which has the client secrets. I did not put/save or use the auth token anywhere in the code. Following is what I remember when I deployed the code for the first time:There was some token which I was asked to paste on the cmd as I was deploying using the command line. After that I could launch my application.After that whenever I deployed my code and launched the application, there was no authorization(which happened the first time) and I could use the application and the API subsequently.Suddenly I started getting invalid credentials error.I was using client_secrets.json generated from the developer's console and I did not provide auth token anywhere in the code and I did not save it either. I read that a refresh token can be used and that can avoid expiration of the auth token.Currently, the issue is that I am getting invalid credentials error and I believe it is because of expiration of auth token. I am not aware about the solution in this case. It is the sample code provided by Google and I believe it automatically handles Oauth authorization (unlike the case where we programmatically call an Oauth url, get a token, store it somewhere and provide it with our subsequent requests). What do I need to do with the sample code so as to resolve this and not to run into the error in future?Also that the application is running fine locally, but the authorization issue comes when it is deployed on the cloud. Thanks. 解决方案 The copy/paste token was actually unrelated to the actual credentials being used by the app for accessing the Calendar API; on the command line, it was the App Engine SDK getting permission to modify your App Engine project by uploading the sample app, regardless of what the app itself does.Now, for the real issue here, it actually seems to be a bug where the GoogleAuthorizationCodeFlow somehow receives an authorization code response which doesn't contain a refreshToken, only an accessToken, and then still goes on to store it in Datastore. Normally, the flow you see is expected to pop up a page saying "This app would like to: ... Have offline access" for the user on the first load, and then your app is supposed to get an accessToken and refreshToken pair, where the accessToken typically expires within 1 hour but then the Credential object knows how to automatically catch the 401 exception and execute the refreshToken to get a new accessToken, all under the hood. This could either be considered a bug in the backend server for returning a credential which lacks a refreshToken, or a bug in the client logic for still assuming there's a refreshToken and thus getting stuck rather than re-issuing a request for access capabilities.Fortunately, there's an easy workaround. Right now, the fact that you're seeing the 401 error means there's a sticky malformed credential stored in your Datastore and possibly also your Memcache. Navigate to your appengine.google.com page, and assuming you aren't already serving a live production-critical web application there, go to Datastore Viewer on the left, look for the Query -> By kind: drop-down menu, to find StoredCredential, check all the items assuming they likely all came from your Calendar sample, and click "Delete". Also navigate to Memcache Viewer on the left-hand-side menu, and then click Flush Cache.Now, it appears the reason the backends are trying to return credentials lacking a refreshToken is due to the client trying to incorrectly make use of "auto-approval". This is evidenced by the fact that when I create a brand-new client id with a new JSON client_secrets, then on just the very first time loading the sample calendar app, I find with my logging statements inside the src/main/java/com/google/api/services/samples/calendar/appengine/server/Utils.java file:Credential credential = newFlow().loadCredential(userId);if (credential.getRefreshToken() != null) { logger.log(Level.SEVERE, "Refresh token is not null");} else { logger.log(Level.SEVERE, "Refresh token is null!");}then I do get a refreshToken for each unique login username I try to access it with. However, if I purge the credential from Datastore and Memcache, forcing a re-authentication on a subsequent request, I stop seeing the approval prompt and my credentials stop having refreshTokens, thus causing them to stop working 1 hour later.Solution (tl;dr)After purging your Datastore for all entities of kind StoredCredential and flushing your Memcache, simply add .setApprovalPrompt("force") to the newFlow() method inside of src/main/java/com/google/api/services/samples/calendar/appengine/server/Utils.java; the method will look like this:static GoogleAuthorizationCodeFlow newFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").setApprovalPrompt("force").build();}Optionally, add some logging where the credential is obtained (it's generally bad practice to log the actual accessToken and especially the refreshToken, but during debugging doing it once or twice probably doesn't hurt :), and re-update your app. You'll find that the first time you access your app, you'll get the approval prompt now, and then it should safely work forever since the refreshToken never expires and is now saved in your Datastore.Note that there's a limit on the number of refreshTokens per client/userId pair ever issued, and if you do purge your Datastore/Memcache during debugging, it effectively leaks a refreshToken. The behavior is just that after 25 such tokens, the earlier ones will automatically be deactivated. See the oauth2 docs for more info on that limit. 这篇关于凭据无效:Google API 日历的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!