總覽
目的:本文件說明如何使用 GoogleCredential 公用程式類別,才能使用 Google 服務執行 OAuth 2.0 授權。適用對象 各項我們所提供的一般 OAuth 2.0 函式相關資訊,請參閱 OAuth 2.0 和適用於 Java 的 Google OAuth 用戶端程式庫。
摘要:如要存取儲存在 Google 服務上的受保護資料,請使用 OAuth 2.0 的授權。 Google API 支援各種用戶端應用程式的 OAuth 2.0 流程。 在這些流程中,用戶端應用程式要求存取權杖 僅與用戶端應用程式與受保護資料的擁有者相關聯 降低頻率存取權杖也與有限範圍相關聯, 定義用戶端應用程式可存取的資料類型 (例如 「管理您的工作」)。OAuth 2.0 的一項重要目標是 方便存取受保護的資料,同時將潛在影響降至最低 萬一存取權杖遭竊。
Java 專用 Google API 用戶端程式庫的 OAuth 2.0 套件是以 一般用途 適用於 Java 的 Google OAuth 2.0 用戶端程式庫。
如需詳細資訊,請參閱下列套件的 Javadoc 文件:
- com.google.api.client.googleapis.auth.oauth2 (來自 google-api-client)
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (來自 google-api-client-appengine)
Google API Console
您必須先在 使用 Google API 控制台進行驗證和帳單 用途包括:無論用戶端是已安裝應用程式、行動應用程式 網路伺服器或在瀏覽器中執行的用戶端
如需正確設定憑證的操作說明,請參閱 API 控制台說明。
憑證
GoogleCredential
GoogleCredential 是適用於 OAuth 2.0 的執行緒安全輔助類別,用於存取受保護的資源 存取權杖舉例來說,如果已有存取權杖 可透過以下方式提出要求:
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Plus plus = new Plus.builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), credential) .setApplicationName("Google-PlusSample/1.0") .build();
Google App Engine 身分
這個替代憑證是基於 Google App Engine App Identity Java API。 用戶端應用程式要求存取 使用者資料,App Identity API 則提供用戶端存取權 自己的資料
使用 AppIdentityCredential (來自 google-api-client-appengine)。 此憑證將簡單許多,因為 Google App Engine 可以包辦所有 詳細資料您只需要指定所需的 OAuth 2.0 範圍即可。
從 urlshortener-robots-appengine-sample 取得的程式碼範例:
static Urlshortener newUrlshortener() { AppIdentityCredential credential = new AppIdentityCredential( Collections.singletonList(UrlshortenerScopes.URLSHORTENER)); return new Urlshortener.Builder(new UrlFetchTransport(), GsonFactory.getDefaultInstance(), credential) .build(); }
資料儲存庫
存取權杖的有效期限通常為 1 小時,之後您
。
GoogleCredential
會自動「重新整理」也就是取得
新的存取權杖做法是使用長期更新權杖
使用存取權杖時,通常會連同存取權杖一併接收
在授權碼流程期間的 access_type=offline
參數 (請參閱
GoogleAuthorizationCodeFlow.Builder.setAccessType(String))。
大多數應用程式都需要保存憑證的存取權杖,並/或 重新整理權杖。如要保留憑證的存取權和/或更新權杖,您可以 讓使用者自行實作 DataStoreFactory 使用 StoredCredential; 您也可以使用程式庫提供的這些實作項目:
- AppEngineDataStoreFactory: 使用 Google App Engine Data Store API 保留憑證。
- MemoryDataStoreFactory: 「persist」憑證中的憑證,只會在程序生命週期中做為短期儲存空間使用。
- FileDataStoreFactory: 將憑證保留在檔案中。
App Engine 使用者: AppEngineCredentialStore 並將於近期移除。建議您使用 AppEngineDataStoreFactory 使用 StoredCredential。 如果您有以舊方式儲存的憑證,可以使用新增的憑證 輔助方法 migrateTo(AppEngineDataStoreFactory) 或 migrateTo(DataStore) 才能進行遷移作業
您可以使用 DataStoreCredentialRefreshListener ,並使用 GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) 設定該憑證)。
授權碼流程
使用授權碼流程,讓使用者授予您的應用程式 透過 Google API 存取受保護資料。本流程的通訊協定是 指定於 授權碼授權。
這個流程是透過 GoogleAuthorizationCodeFlow 進行實作。 步驟如下:
- 使用者登入您的應用程式。您需要連結該使用者 並提供應用程式專屬的 User-ID。
- 呼叫 AuthorizationCodeFlow.loadCredential(String)) 檢查使用者的憑證是否已經存在。 如果有,就沒問題了。
- 如果沒有,請呼叫 AuthorizationCodeFlow.newAuthorizationUrl() 並將使用者的瀏覽器導向授權頁面,以便授予 存取受保護的資料。
- 接著,Google 授權伺服器會將瀏覽器重新導回
應用程式指定的重新導向網址,以及
code
查詢 參數。透過code
參數以使用 AuthorizationCodeFlow.newTokenRequest(String))。 - 使用 AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String)) 儲存及取得存取受保護資源所需的憑證。
或者,如果您不是使用 GoogleAuthorizationCodeFlow,可以使用較低層級的類別:
- 使用 DataStore.get(String),根據使用者 ID 從商店載入憑證。
- 使用 GoogleAuthorizationCodeRequestUrl 將瀏覽器導向授權頁面。
- 使用 AuthorizationCodeResponseUrl 處理授權回應並剖析授權碼。
- 請使用 GoogleAuthorizationCodeTokenRequest 要求存取權杖,或可能更新憑證。
- 建立新的 GoogleCredential,並使用 DataStore.set(String, V) 儲存。
- 使用
GoogleCredential
存取受保護的資源。如果有的話,系統會使用更新權杖自動重新整理過期的存取權杖。請務必使用 DataStoreCredentialRefreshListener,並使用 GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) 將憑證設為憑證)。
在 Google API 控制台設定專案時, 系統會根據您使用的流程,選擇不同的憑證 詳情請參閱設定 OAuth 2.0 和 OAuth 2.0 情境。 各流程的程式碼片段如下。
網路伺服器應用程式
如要瞭解這個流程的通訊協定,請參閱 針對網路伺服器應用程式使用 OAuth 2.0。
這個程式庫提供 CSP 輔助類別,以大幅簡化 基本用途的授權碼流程。您只需提供具體的子類別 AbstractAuthorizationCodeServlet 和 AbstractAuthorizationCodeCallbackServlet (來自 google-oauth-client-servlet) 然後新增到 web.xml 檔案中請注意,您仍然需要為使用者 登入網頁應用程式,並擷取使用者 ID。
public class CalendarServletSample extends AbstractAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), GsonFactory.getDefaultInstance(), "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]", Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), GsonFactory.getDefaultInstance() "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]", Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
Google App Engine 應用程式
App Engine 上的授權碼流程與 JDK 幾乎相同 但可以使用 Google App Engine 的 Users Java API。該使用者 必須登入,才能啟用 Users Java API;深入瞭解 如果使用者尚未登入,則將他們重新導向至登入頁面,請參閱 安全性和驗證 (在 web.xml 中)。
與 JDK 案例的主要差異在於
子類別
AbstractAppEngineAuthorizationCodeServlet 和 AbstractAppEngineAuthorizationCodeCallbackServlet
(來自 google-oauth-client-appengine。
他們會擴充抽象 JDK 類別,並實作 getUserId
方法
使用 Users Java APIAppEngineDataStoreFactory
(來自 google-http-client-appengine)
使用 Google App Engine 資料來保留憑證
Store API。
從 calendar-appengine-sample 取得的範例 (稍有修改):
public class CalendarAppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return Utils.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return Utils.newFlow(); } } class Utils { static String getRedirectUri(HttpServletRequest req) { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } static GoogleAuthorizationCodeFlow newFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } } public class OAuth2Callback extends AbstractAppEngineAuthorizationCodeCallbackServlet { private static final long serialVersionUID = 1L; @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname(); resp.getWriter().print("<h3>" + nickname + ", why don't you want to play with me?</h1>"); resp.setStatus(200); resp.addHeader("Content-Type", "text/html"); } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return Utils.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return Utils.newFlow(); } }
如需更多範例,請參閱 storage-serviceaccount-appengine-sample.
服務帳戶
GoogleCredential 也支援服務帳戶 用戶端應用程式要求存取 使用者資料,可存取用戶端應用程式的 要保護自身資料您的用戶端應用程式會使用 從 Google API 控制台下載私密金鑰。
從 plus-serviceaccount-cmdline-sample 取得的程式碼範例:
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); JsonFactory jsonFactory = GsonFactory.getDefaultInstance(); ... // Build service account credential. GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json")) .createScoped(Collections.singleton(PlusScopes.PLUS_ME)); // Set up global Plus instance. plus = new Plus.Builder(httpTransport, jsonFactory, credential) .setApplicationName(APPLICATION_NAME).build(); ...
如需更多範例,請參閱 storage-serviceaccount-cmdline-sample.
冒用他人身分
您也可以透過服務帳戶流程,模擬 您的機構。這與上方的服務帳戶流程非常類似 此外,請呼叫 GoogleCredential.Builder.setServiceAccountUser(String)。
已安裝的應用程式
這是指令列授權碼流程,詳情請參閱針對已安裝的應用程式使用 OAuth 2.0 一文。
程式碼片段範例 plus-cmdline-sample:
public static void main(String[] args) { try { httpTransport = GoogleNetHttpTransport.newTrustedTransport(); dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR); // authorization Credential credential = authorize(); // set up global Plus instance plus = new Plus.Builder(httpTransport, JSON_FACTORY, credential).setApplicationName( APPLICATION_NAME).build(); // ... } private static Credential authorize() throws Exception { // load client secrets GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(PlusSample.class.getResourceAsStream("/client_secrets.json"))); // set up authorization code flow GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( httpTransport, JSON_FACTORY, clientSecrets, Collections.singleton(PlusScopes.PLUS_ME)).setDataStoreFactory( dataStoreFactory).build(); // authorize return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user"); }
用戶端應用程式
如要使用瀏覽器型用戶端流程,請參閱 針對用戶端應用程式使用 OAuth 2.0, 通常按照下列步驟操作:
- 使用以下方式,在瀏覽器中將使用者重新導向授權頁面: GoogleBrowserClientRequestUrl 授予瀏覽器應用程式存取使用者受保護資料的權限。
- 使用 JavaScript 適用的 Google API 用戶端程式庫 處理在重新導向 URI 網址片段中找到的存取權杖 已在 Google API 控制台註冊。
網頁應用程式的使用範例:
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException { String url = new GoogleBrowserClientRequestUrl("812741506391.apps.googleusercontent.com", "https://oauth2.example.com/oauthcallback", Arrays.asList( "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile")).setState("/profile").build(); response.sendRedirect(url); }
Android
要搭配 Android 使用的程式庫:
如果您要開發 Android 應用程式,且當中包含要使用的 Google API 的 Google Play 服務程式庫 以獲得最佳效能和體驗。如果您是 Google API 您想使用 Android 系統並未納入 Google Play 服務程式庫, 可使用支援 Android 4.0 (Ice Cream Sandwich) 的 Google API 用戶端程式庫 (或更高版本),請參閱本文說明。透過 Google Cloud 控制台 Java 適用的 API 用戶端程式庫是 @Beta 版。
背景說明:
自 Eclair (SDK 2.1) 起,系統會在 Android 裝置上管理使用者帳戶 與客戶經理聯繫所有 Android 應用程式授權都是透過單一介面管理 由 SDK 管理 AccountManager (帳戶管理員)。 您可以指定應用程式所需的 OAuth 2.0 範圍,然後傳回存取權 要使用的符記
OAuth 2.0 範圍是透過 authTokenType
參數指定,為 oauth2:
加上範圍例如:
oauth2:https://www.googleapis.com/auth/tasks
這麼做會指定 Google Tasks API 的讀取/寫入權限。如果您需要多個 OAuth 2.0 範圍,請使用以空格分隔的清單。
部分 API 提供特殊的 authTokenType
參數。例如:
「管理您的工作」是上述 authtokenType
範例的別名。
您也必須指定 Google API 控制台。 否則,AccountManager 只會為您提供 而且通常非常低相對地 金鑰獲得較高的免費配額,並可視需要設定帳單的使用情形 上方。
擷取的程式碼片段範例 tasks-android-sample:
com.google.api.services.tasks.Tasks service; @Override public void onCreate(Bundle savedInstanceState) { credential = GoogleAccountCredential.usingOAuth2(this, Collections.singleton(TasksScopes.TASKS)); SharedPreferences settings = getPreferences(Context.MODE_PRIVATE); credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null)); service = new com.google.api.services.tasks.Tasks.Builder(httpTransport, jsonFactory, credential) .setApplicationName("Google-TasksAndroidSample/1.0").build(); } private void chooseAccount() { startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_GOOGLE_PLAY_SERVICES: if (resultCode == Activity.RESULT_OK) { haveGooglePlayServices(); } else { checkGooglePlayServicesAvailable(); } break; case REQUEST_AUTHORIZATION: if (resultCode == Activity.RESULT_OK) { AsyncLoadTasks.run(this); } else { chooseAccount(); } break; case REQUEST_ACCOUNT_PICKER: if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) { String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME); if (accountName != null) { credential.setSelectedAccountName(accountName); SharedPreferences settings = getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putString(PREF_ACCOUNT_NAME, accountName); editor.commit(); AsyncLoadTasks.run(this); } } break; } }