程式設計師的長安十二時辰:Java實現從Google oauth2.0認證呼叫谷歌內部api

泡沫同學發表於2019-07-27

最近公司在做一個app購買的功能,主要思路就是客戶在app上購買套餐以後,Google自動推送訊息到Java後端,然後Java後端通過訂單的token獲取訂單資訊,儲存到資料庫。

Java後端要獲取訂單資訊,除了一個訂單token還不夠,還需要通過goole的oauth驗證拿到一個accessToken才行,但是怎麼才能獲取到accessToken呢,

在這個過程中,我採坑無數,終於在偉大的同性交友網站GitHub上面找到了答案。

首先,網上的教程一般都是這樣的:

  一. 在Google Developer Console中建立一個Oauth客戶端ID,選擇Web Application賬戶,得到client_id,client_secret 和 redirect_uri,這3個引數後邊步驟常用到(此為前提)

        二. 使用上一步獲取到的引數獲取Authorization code 

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher
&response_type=code
&access_type=offline
&redirect_uri={REDIRECT_URIS}
&client_id={CLIENT_ID}

        我們需要將這個URL以瀏覽器的形式開啟,這時會跳出提示你Sign in with your Google Account,然後在用有project授權的谷歌賬戶登入,位址列會出現我們所需的code。例如:https://www.example.com/oauth2callback?code=4/CpVOd8CljO_gxTRE1M5jtwEFwf8gRD44vrmKNDi4GSS.kr-GHuseD-oZEnp6UADFXm0E0MD3FlAI

        三. 利用code 獲取access_token,refresh_token

https://accounts.google.com/o/oauth2/token?
code={CODE}
&client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&redirect_uri={REDIRECT}
&grant_type=authorization_code
 

        我們這一步的目的是獲取refresh_token,只要有了這個長效token,access_token是隨時可以獲取的,第一次發起請求得到的JSON字串如下所示,以後再請求將不再出現refresh_token,要儲存好。expires_in是指access_token的時效,為3600秒。

{
    "access_token": "ya29.3gC2jw5vm77YPkylq0H5sPJeJJDHX93Kq8qZHRJaMlknwJ85595eMogL300XKDOEI7zIsdeFEPY6zg", 
    "token_type": "Bearer", 
    "expires_in": 3600, 
    "refresh_token": "1/FbQD448CdDPfDEDpCy4gj_m3WDr_M0U5WupquXL_o"
}

        四. 進一步可利用refresh_token獲取新的access_token

https://accounts.google.com/o/oauth2/token?
grant_type=refresh_token
&client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&refresh_token={REFRESH_TOKEN}

        五. 使用access_token 呼叫Google API 達到最終目的(如果access_token過時,回到第四步)

  然後使用accessToken即可呼叫谷歌的API了,例如要檢視訂單的購買資訊

  https://www.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionName}/tokens/ {purchaseToken}?access_token={accessToken}

然後看起來是不是很簡單,很方便?

可是我要在Java後端實現這些操作,卻沒有想象中這麼簡單了,首先第一步,獲取Authorization code這塊就犯了難,後端發起一個請求倒是不難,可是用有project授權的谷歌賬戶登入授權,這塊真的是想大喊一聲:臣妾做不到啊!

那還能怎麼辦?當然不能就此罷休,在這裡還是感謝萬能的度娘,終於讓我發現了plan B:

建立一個服務賬戶,通過賬戶的祕鑰來獲取token,簡要步驟如下:

1.獲取服務賬戶 Service Account

2.建立訪問程式,載入Service Account檔案,獲取token並訪問請求API

是不是很簡單?很nice?來來我們上圖說話:

1.首先選中我們創造的Oauth客戶端,然後點選建立服務賬戶祕鑰

2.選擇app Engine,Json格式

3.ok了,然後我們再使用如下的程式碼載入服務賬戶,獲取accessToken

import java.io.FileInputStream;
import java.util.Arrays;
import java.util.List;

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;

public class GoogleOAuth2ServiceAccountSample {

    /** OAuth 2.0 scopes. 重要,規範訪問者的檢視範圍*/
    private static final List<String> SCOPES = Arrays.asList(
            "https://www.googleapis.com/auth/androidpublisher");

    public static void main(String[] args) {
        try {
            // 根據Service Account檔案構造認證例項 GoogleCredential
            GoogleCredential credential = GoogleCredential
                    .fromStream(new FileInputStream(
                            "Google_Wallet-94e38f1f23f7.json"))// 載入服務帳戶認證檔案
                    .createScoped(SCOPES);

            // 重新整理token
            credential.refreshToken();

            // 獲取token
            System.out.println(credential.getAccessToken());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.這樣我們就能獲取到accessToken了嗎?是的,我拿到了心心念唸的accessToken,可是在我使用token獲取訂單詳情的時候,卻報錯了。

  "errors": [
   {
    "domain": "androidpublisher",
    "reason": "permissionDenied",
    "message": "The current user has insufficient permissions to perform the requested operation."
   }
  ],
  "code": 401,
  "message": "The current user has insufficient permissions to perform the requested operation."
 }
}

說實話到這一步,我已經有些氣餒了,谷歌的官方文件看了N遍,也沒有頭緒,到底該怎麼辦呢?

沒辦法,只能繼續google查詢資料,畢竟國外的資料可能多一些,直到我看到這一篇文章:

5.好吧 看到這塊我是如獲至寶,趕緊操作起來,開啟https://play.google.com/apps/publish,邀請我註冊的服務賬戶的郵箱,並給予管理訂單和檢視財務資料的許可權,ok!

 

6.然後就ok了嗎?我興致勃勃的又試了一下請求獲取訂單詳情的api,oh no!又是該死的報錯,和上面的一模一樣:

  "errors": [
   {
    "domain": "androidpublisher",
    "reason": "permissionDenied",
    "message": "The current user has insufficient permissions to perform the requested operation."
   }
  ],
  "code": 401,
  "message": "The current user has insufficient permissions to perform the requested operation."
 }
}
7.我徹底無奈了,短短的三天已經過去了,對於這麼簡單的小功能我卻拿不下,實在有點羞愧難當,難道就這樣放棄了嗎?不!我永不言敗!
然後就繼續常規操作,google用英文關鍵字查詢,雖然本人英文很渣,但是靠著強大的谷歌翻譯,還是能看懂七八分,哈哈!功夫不負有心人,靠著我頑強的毅力
終於讓我看到了一個答案:


what?
谷歌,你是我哥,真的!還有這種操作,賬戶更改需要24小時才能生效,word媽!
好吧 ,既然故事到了這裡,我就只能等吧,不就是24小時麼,就當是我程式設計師的長安十二時辰了,我等!
一夜無眠。
翌日,我熟悉的啟動IDEA,啟動tomcat,心要跳到了嗓子眼,這一刻時間彷彿凝固了,我發起請求,一串期待已久的json字串出現在我眼前:
{
 "kind": "androidpublisher#subscriptionPurchase",
 "startTimeMillis": "1564021170426",
 "expiryTimeMillis": "1564023267679",
 "autoRenewing": false,
 "priceCurrencyCode": "HKD",
 "priceAmountMicros": "949000000",
 "countryCode": "HK",
 "developerPayload": "",
 "cancelReason": 1,
 "orderId": "GPA.3309-8698-7096-01554..5",
 "purchaseType": 0,
 "acknowledgementState": 1
}
真的是激動的心,顫抖的手,就問兄弟你有沒有!哈哈!我可以仰天大笑出門去,我輩豈是蓬蒿人!
快哉!!!!
好了,這個問題就告一段落了,在這裡在標註一下這個過程常見的一些問題,如果有道友也遇到,希望可以解憂!

問題1: projectNotLinked

{
    "error": {
        "errors": [
            {
                "domain": "androidpublisher",
                "reason": "projectNotLinked",
                "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
            }
        ],
        "code": 403,
        "message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
    }
}

在這個頁設定關聯:https://play.google.com/apps/publish/

ps:注意登陸賬戶必須是app所有者

Google Play Developer Console

1.  "Google Play Developer Console" > "Settings" > subcategory "API access".

2.  Make a link to your "Linked Project".

3.  "Service Account" place maybe already showing ur "Service account" CLIENT ID which made "google developer console".

 

相關文章