總覽
目的:本文件說明 Google OAuth 用戶端程式庫您可以運用這些函式 驗證及授權。
如需使用 GoogleCredential
執行 OAuth 2.0 授權的操作說明
Google 服務,請參閱
將 OAuth 2.0 與適用於 Java 的 Google API 用戶端程式庫搭配使用。
摘要: OAuth 2.0 是 可讓使用者安全地授權用戶端的標準規格 存取受保護的伺服器端資源。此外, OAuth 2.0 不記名權杖 規格說明如何使用存取權 取得的權杖
如需詳細資訊,請參閱下列套件的 Javadoc 文件:
- com.google.api.client.auth.oauth2 (來自 google-oauth-client)
- com.google.api.client.extensions.servlet.auth.oauth2 (from google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
客戶註冊
使用 Java 適用的 Google OAuth 用戶端程式庫之前,您可能需要 透過授權伺服器註冊您的應用程式,以便接收用戶端 ID 和 用戶端密鑰。(如需關於此程序的一般資訊,請參閱 客戶 註冊規格)。
憑證和憑證存放區
認證
是一種執行緒安全 OAuth 2.0 輔助類別,用於存取受保護的資源,並使用
存取權杖使用更新權杖時,Credential
也會重新整理存取權
權杖。舉例來說,如果您在
已有存取權杖,您可以透過下列方式提出要求:
public static HttpResponse executeGet( HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url) throws IOException { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); return requestFactory.buildGetRequest(url).execute(); }
大多數應用程式都需要保存憑證的存取權杖, 更新權杖,以免日後無法重新導向至授權 網頁。 CredentialStore 這個程式庫中的實作方式已淘汰,將於日後移除 版本。另一種方法是使用 DataStoreFactory 和 DataStore 介面集 StoredCredential、 編碼器-解碼器架構 適用於 Java 的 Google HTTP 用戶端程式庫。
您可以使用程式庫提供的下列任一實作項目:
- JdoDataStoreFactory 使用 AWS 保留憑證。
- AppEngineDataStoreFactory 使用 Google App Engine Data Store API 保留憑證。
- MemoryDataStoreFactory 「persist」因此即使僅執行短期憑證 都能儲存整個物件
- FileDataStoreFactory 將憑證保留在檔案中。
Google App Engine 使用者:
AppEngineCredentialStore 已淘汰,並即將移除。
建議您使用 AppEngineDataStoreFactory 使用 StoredCredential。 如果您以舊方式儲存憑證,可以使用新增的輔助方法。 migrateTo(AppEngineDataStoreFactory) 或 migrateTo(DataStore) 以便進行遷移
使用 DataStoreCredentialRefreshListener 並將其設為 GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
授權碼流程
使用授權碼流程,讓使用者授予您的應用程式 保護資料的存取權這個流程的通訊協定已在 授權碼授權規格。
這個流程是以 AuthorizationCodeFlow。 步驟如下:
- 使用者登入您的應用程式。您需要將該使用者與 應用程式專屬的 User-ID。
- 致電 AuthorizationCodeFlow.loadCredential(String), 檢查使用者的憑證是否已經存在。 如果有回應,就沒問題了。
- 如果沒有,請呼叫 AuthorizationCodeFlow.newAuthorizationUrl() 並將使用者的瀏覽器導向授權頁面,讓他們在這個頁面中 應用程式存取受保護的資料。
- 接著,網路瀏覽器會重新導向至含有「程式碼」的重新導向網址。項查詢 參數,可用來要求存取權杖 AuthorizationCodeFlow.newTokenRequest(String)。
- 使用 AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) 儲存及取得存取受保護資源所需的憑證。
如果您不是使用 AuthorizationCodeFlow、 您可以使用較低層級的類別:
- 使用 DataStore.get(String) 根據使用者 ID,從商店載入憑證。
- 使用 AuthorizationCodeRequestUrl 將瀏覽器導向授權網頁。
- 使用 AuthorizationCodeResponseUrl 來處理授權回應並剖析授權碼。
- 使用 AuthorizationCodeTokenRequest 要求存取權杖和更新權杖。
- 建立新的 Credential,並使用 DataStore.set(String, V) 儲存。
- 使用憑證存取受保護的資源。 過期的存取權杖會自動使用更新權杖重新整理。 請務必使用 DataStoreCredentialRefreshListener 並將其設為 Credential.Builder.addRefreshListener(CredentialRefreshListener).
JS 授權碼流程
這個程式庫提供 CSP 輔助類別,以大幅簡化 基本用途的授權碼流程。您只需提供具體的子類別 / AbstractAuthorizationCodeServlet 和 AbstractAuthorizationCodeCallbackServlet (來自 google-oauth-client-servlet) 然後新增到 web.xml 檔案中請注意,您仍然需要為使用者 登入網頁應用程式,並擷取使用者 ID。
程式碼範例:
public class ServletSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class ServletCallbackSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .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)。它們會擴充抽象 xls 類別,並使用 Users Java API 為您實作 getUserId
方法。AppEngineDataStoreFactory (來自 Java 適用的 Google HTTP 用戶端程式庫) 使用 Google App Engine Data Store API 保存憑證是個好方法。
程式碼範例:
public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } } public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } }
指令列授權碼流程
簡化的範例程式碼 dailymotion-cmdline-sample:
/** Authorizes the installed application to access user's protected data. */ private static Credential authorize() throws Exception { OAuth2ClientCredentials.errorIfNotSpecified(); // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), new ClientParametersAuthentication( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) .setDataStoreFactory(DATA_STORE_FACTORY).build(); // authorize LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } private static void run(HttpRequestFactory requestFactory) throws IOException { DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites"); url.setFields("id,tags,title,url"); HttpRequest request = requestFactory.buildGetRequest(url); VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class); ... } public static void main(String[] args) { ... DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); final Credential credential = authorize(); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { credential.initialize(request); request.setParser(new JsonObjectParser(JSON_FACTORY)); } }); run(requestFactory); ... }
瀏覽器用戶端流程
以下是瀏覽器用戶端流程中,指定的 Implicit Grant 規格:
- 使用 BrowserClientRequestUrl 時, 將使用者的瀏覽器重新導向至授權頁面,讓使用者在這個頁面中 授予應用程式存取受保護資料的權限。
- 使用 JavaScript 應用程式處理網址中的存取權杖 位於授權伺服器註冊的重新導向 URI 片段。
網頁應用程式的使用範例:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = new BrowserClientRequestUrl( "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz") .setRedirectUri("https://client.example.com/cb").build(); response.sendRedirect(url); }
偵測過期的存取權杖
根據 OAuth 2.0 不記名規格,
伺服器呼叫伺服器存取受保護的資源,但存取權已過期時
伺服器通常會傳回 HTTP 401 Unauthorized
狀態碼
例如:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
然而,規格中的彈性似乎很大。適用對象 詳細資料,請參閱 OAuth 2.0 供應商的說明文件。
另一種方法是檢查以下程式碼中的 expires_in
參數:
存取權杖回應。
這會指定授予存取權杖的生命週期 (以秒為單位),
通常一小時。但是,存取權杖實際上可能不會在最後過期
,而伺服器可能會繼續允許存取。正因如此
一般而言,我們會建議您等待 401 Unauthorized
狀態碼,而非
並假設憑證已過期。此外,也可以
如果存取權杖到期,請盡快重新整理;如果存取權杖
無法使用,請繼續使用存取權杖,直到您收到 401
為止。這個
是 Google Analytics 中預設採用的策略
憑證。
另一種方法是在每個請求之前擷取新的存取權杖,但 每次都必須向權杖伺服器發出額外的 HTTP 要求,因此很有可能 但速度和網路用量選擇不當在理想情況下 建立在安全的永久儲存空間,可盡量減少應用程式對新存取的要求數量 符記(但是,對於已安裝的應用程式而言,安全儲存空間是個很困難的問題)。
請注意,除了到期時間外,存取權杖可能會失效的原因, 例如使用者已明確撤銷權杖 錯誤處理程式碼是完善的。系統偵測到 有效,例如已過期或撤銷,您必須移除存取權 取得儲存空間憑證以 Android 裝置為例,您必須呼叫 AccountManager.invalidateAuthToken。