Java OAuth 2.0 客戶端程式設計(三):認證碼授權

developerworks發表於2015-08-13

Java OAuth 2.0 客戶端程式設計(一): 資源所有者密碼憑據授權

Java OAuth 2.0 客戶端程式設計(二): 客戶端憑據授權

Java OAuth 2.0 客戶端程式設計(三):認證碼授權

概述

OAuth 是一個開放的授權標準,允許客戶端代表一個資源所有者獲得訪問受保護伺服器資源的訪問權。資源所有者可以是另一個客戶端或終端使用者。OAuth 還可以幫助終端使用者將對其伺服器資源的訪問許可權授權給第三方,而不必共享其憑據,比如使用者名稱和密碼。本系列文章遵從 RFC6749 中列出的 OAuth 2.0 授權框架。可以在 Internet Engineering Task Force 的網站上找到 RFC 6749 中列出的完整 OAuth 2.0 授權框架(請參閱 參考資料)。

授權批准

授權批准是一種憑據,可代表資源所有者用來獲得訪問受保護資源的訪問權。客戶端使用此憑據獲取訪問令牌,而且此訪問令牌最終將與請求一起傳送,以便訪問受保護的資源。OAuth 2.0 定義了四種授權型別:

  1. 授權碼
  2. 隱式
  3. 資源所有者密碼憑據
  4. 客戶端憑據

這個文章系列由四部分組成,將會引導您使用上面列出的每種授權型別在 Java™ 程式設計中實現 OAuth 2.0 客戶端。在第 3 部分中,我將解釋如何實現認證碼授權。本文詳細介紹此類授權,並解釋示例客戶端程式碼,此程式碼可用於相容 OAuth 2.0 的任何伺服器介面,以便支援此授權。在本文的最後,您應該對客戶端實現有全面的瞭解,並準備好下載示例客戶端程式碼,自己進行測試。

認證碼授權

此授權已針對機密性客戶端進行了優化,用於獲得訪問令牌和重新整理令牌。這是一個基於重定向的流程,因此,客戶端必須能夠與資源所有者的使用者代理(通常是 Web 瀏覽器)進行互動,並且還必須能夠(通過重定向)接收來自授權伺服器的傳入請求。

認證碼授權如 圖 1 所示。

圖 1. 授權碼流程

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

圖 1 中所示的流程包括以下步驟:

  • (A) 客戶端(通常是一個 Web 應用程式)發起流程,將資源所有者的使用者代理(通常是 Web 瀏覽器)定位到授權端點。客戶端的請求包括客戶端識別符號、請求範圍、本地狀態和一個重定向 URI。在訪問獲得批准(或拒絕)之後,授權伺服器將使用者代理(通常是 Web 瀏覽器)定向回到重定向 URI。
  • (B) 資源所有者通過使用者代理對授權伺服器進行身份驗證,並批准或拒絕客戶端的訪問請求。
  • (C) 如果資源所有者批准了訪問請求,授權伺服器將會使用先前(在請求或客戶端註冊時)提供的重定向 URI 將使用者代理(通常是 Web 瀏覽器)重定向回客戶端。重定向 URI 包括客戶之前提供的授權碼以及所有本地狀態。
  • (D) 客戶端從授權伺服器的令牌端點發出訪問令牌請求,其中包括上一步中收到的授權碼。在發出請求時,客戶端使用客戶端憑據與授權伺服器進行身份驗證。客戶端還包括用於獲得驗證授權碼的重定向 URI。
  • (E) 授權伺服器對客戶端進行身份驗證。它驗證授權碼,並確保所收到的重定向 URI 匹配在步驟 (C) 中用於重定向客戶端的 URI。如果有效,授權伺服器將會返回訪問令牌作為響應,並且如果請求離線訪問,可以返回重新整理令牌。

授權碼請求

對應於步驟 (A) 和 (B) 的授權碼請求如 圖 1 所示。在步驟 (A) 中,客戶端採用 application/x-www-form-urlencoded 格式向授權伺服器發出一個請求,如 清單 1 所示。

清單 1. 授權碼請求的示例
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

該請求必須包含以下引數:

  • response_type:必選項。該值必須設定為 code
  • client_id:必選項。客戶端 ID。
  • redirect_uri:必選項。用於使用者代理重定向。
  • scope:可選項。訪問請求的範圍。
  • state:可選項。保持請求和回撥之間的狀態。

在授權伺服器驗證請求後,伺服器將一個 HTTP 重定向程式碼 302 響應傳送回客戶端。該響應還將在 http Location 標頭中包括一個重定向 URI。在步驟 (B) 中,客戶端必須將使用者代理(通常是 Web 瀏覽器)重定向到此 URI。這種重定向 URI 通常是一個登入頁面,資源所有者可以使用其憑據進行登入,並批准/撤銷客戶端的訪問請求。

授權碼響應

授權碼響應該如 圖 1 的步驟 (C) 中所示。如果資源所有者批准了訪問請求,授權伺服器會發出一個授權碼。授權伺服器將使用者代理重定向到步驟 (A) 中作為請求的一部分的重定向 URI,並將授權碼包含為重定向 URI 的查詢元件的一部分,這裡採用的是 application/x-www-form-urlencoded 格式。

URI 引數如下:

  • Code:必選項。由授權伺服器生成的授權碼。該程式碼是臨時的,並且必須在生成後很快過期。客戶不得多次使用授權碼。使用相同程式碼進行的任何進一步請求都應該被授權伺服器撤銷。授權碼被繫結到客戶端識別符號和重定向 URI。
  • State:必選項。如果客戶端的授權碼請求中存在 state 引數,此引數必須設定為與從客戶端接收的值完全相同。

訪問令牌請求

這對應於 圖 1 中的步驟 (D)。客戶端採用 application/x-www-form-urlencoded 格式向令牌端點(授權伺服器)發出一個請求,如 清單 2 所示。

清單 2. 訪問令牌請求的示例
POST /token HTTP/1.1
Host: server.example.com
Authorization:Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

             grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
             &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom&client_id=c342

訪問令牌請求必須設定下列引數:

  • grant_type:必選項。該值必須設定為 authorization_code
  • client_id:必選項。客戶端 ID。
  • client_secret:可選項。密碼,用於與授權伺服器進行身份驗證。
  • code:必選項。從伺服器收到的授權碼。
  • redirect_uri:必選項。在步驟 (A) 中傳送的完全一樣。

授權伺服器驗證該程式碼和重定向 URI 是有效的。在存在機密性客戶端的情況下,授權伺服器也使用在其請求的主體或 Authorization 標頭中傳遞的客戶端憑據來對客戶端進行身份驗證。

訪問令牌響應

這對應於 圖 1 中的步驟 (E)。如果訪問令牌請求是有效的,而且獲得了授權,授權伺服器會在一個訪問令牌響應中返回訪問令牌。成功的響應示例如 清單 3 所示。

清單 3. 成功的訪問令牌響應示例
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"Bearer",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

如果請求無效,或者未經授權,那麼授權伺服器將會使用程式碼返回一個相應的錯誤訊息。

重新整理訪問令牌請求

這是一個可選步驟,如果客戶端請求離線訪問,並在訪問令牌請求中提供一個 refresh_token,則可以此阿勇此步驟。訪問令牌是暫時性的,通常在一個小時後到期。訪問令牌到期後,客戶端需要重複身份驗證過程,資源所有者需要進行登入,並提供授權,讓客戶可以再次發出訪問令牌請求。

如果客戶需要重新整理訪問令牌,而資源所有者沒有位於瀏覽器上,無法進行登入和身份驗證,則客戶端可以採用離線訪問。客戶端可以在發出第一個授權碼請求時請求離線訪問(參見步驟 (A))。根據這項計劃,除了訪問令牌之外,授權伺服器還會返回重新整理令牌。重新整理令牌是一個長壽令牌,不會過期,除非明確由資源所有者撤銷。每當訪問令牌到期時,客戶端可以使用重新整理令牌來重新生成一個訪問令牌,資源所有者無需登入和授權訪問請求。

客戶端採用 application/x-www-form-urlencoded 格式向令牌端點(授權伺服器)發出一個請求,如 清單 4 所示:

清單 4. 請求令牌端點
POST /token HTTP/1.1
Host: server.example.com
Authorization:Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

請求引數的定義如下:

  • grant_type:必選項。該值必須設定為 refresh_token
  • refresh_token:必選項。這是之前從訪問令牌請求獲得的令牌。
  • scope:可選項。訪問請求的範圍。

授權伺服器驗證重新整理令牌併發出一個新的訪問令牌。

重新整理訪問令牌響應

如果請求成功,授權伺服器將會返回一個新的訪問令牌。成功的響應示例如 清單 5 所示。

清單 5. 重新整理訪問令牌響應
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"Bearer",
  "expires_in":3600,
  "example_parameter":"example_value"
}

如果請求無效,或者未經授權,那麼授權伺服器將會使用程式碼返回一個相應的錯誤訊息。

 

設定

示例 Outh2.0 客戶端是一個動態的 Web 專案。您可以從 下載 部分下載包含專案和原始碼的 .war 檔案。將 .war 檔案匯入您的 Eclipse 環境。

先決條件

您需要安裝 Eclipse IDE for Java EE developers,以便設定開發環境,並匯入附加專案。您可以從 Eclipse 下載頁面 下載 Eclipse。

您需要 Apache Tomcat JSP/Servlet 容器來執行 OAuth 2.0 Web 客戶端。您可以從 Apache Tomcat 下載頁面 下載 Tomcat。

依賴關係

客戶端程式碼專案取決於以下 JAR 檔案:

  1. commons-codec-1.6.jar
  2. commons-logging-1.1.1.jar
  3. httpclient-4.2.5.jar
  4. httpclient-cache-4.2.5.jar
  5. httpcore-4.2.4.jar
  6. httpmime-4.2.5.jar
  7. json-simple-1.1.1.jar

第 1 至 6 點中提到的 JAR 檔案可以在 HttpComponents JAR 檔案中找到,您可以從 HttpComponents Downloads 下載頁面 下載該檔案。可以從 JSON Simple 專案 下載 json-simple-1.1.1.jar 檔案。確保這些 jar 檔案被複制到 Apache Tomcat 安裝目錄的 lib 資料夾中。Tomcat 中預設情況下可能已經提供了一些必要的 JAR 檔案,所以您只需複製缺少的檔案即可。

將 project .war 檔案匯入 Eclipse

在安裝了 Eclipse 和 Apache Tomcat 之後,需要匯入來自 下載 部分的 .war 檔案。為了匯入 war 檔案,請遵循 Eclipse 網站的 “匯入 Web 壓縮 (WAR) 檔案” 中列出的這些簡單步驟。

在將 .war 檔案匯入到 Eclipse 時,請確保關聯了 Tomcat 伺服器與您的專案。您需要選擇 Tomcat 版本,並提供 Tomcat 安裝根目錄的路徑,以便完成配置。

您可以在 “建立一個 Tomcat 伺服器 ” 中瞭解有關如何關聯 Tomcat 伺服器與專案的更詳細說明。

在成功將 .war 檔案匯入到 Eclipse 工作區後,您會在專案層次中的 Java Resources/src 下找到原始碼。

執行 OAuth 2.0 客戶端

在成功匯入 .war 檔案並使用必要的 JAR 檔案設定 Tomcat 之後,您可以執行客戶端:右鍵單擊專案名稱 OAuth2.0_AuthCode 並選擇 Run As 和 Run on Server

這會將客戶端部署到伺服器,並在 Eclipse 內部瀏覽器中載入 index.html 頁面。就個人而言,我更喜歡在外部瀏覽器上與 Web 客戶端進行互動。

您可以從任何瀏覽器導航至以下網址,訪問 Web 客戶端: http://localhost:8080/OAuth2.0_AuthCode

後續章節會詳細解釋客戶端程式碼,並告訴您如何使用流行的 OAuth 2.0 相容的伺服器(如 Salesforce、Google 和 IBM)測試這個客戶端。

OAuth 2.0 客戶端程式碼

本文中的示例 OAuth 2.0 客戶端實現了認證碼授權。示例客戶端程式碼是一個 Web 應用程式,而不是一個普通的 Java 專案,後者適用於前面文章中所討論的授權型別。這是因為,認證碼授權流程的目的是滿足 Web 應用程式,並針對 Web 瀏覽器的使用者代理進行了優化。

輸入引數

使用在專案的 src 資料夾中提供的 Oauth2Client.config 屬性檔案向客戶端提供所需的輸入引數。輸入引數是:

  • scope:這是一個可選引數。它代表訪問請求的範圍。由伺服器返回的訪問令牌只可以訪問在 scope 中提到的那些服務。
  • state:這是一個可選引數,用於維持客戶端請求和來自授權伺服器的重定向響應之間的狀態,目的是確保客戶端的完整性。
  • grant_type:需要將這個引數設定為 authorization_code,表示認證碼授權型別。
  • client_id:註冊應用程式時由資源伺服器提供的客戶端或使用者 ID。
  • client_secret:註冊應用程式時由資源伺服器提供的客戶端或使用者的密碼。
  • access_token:令牌端點響應有效的和經過授權的訪問令牌請求時所返回的訪問令牌。由授權伺服器返回的授權程式碼用於交換訪問令牌。
  • refresh_token:令牌端點響應有效的和經過授權的訪問令牌請求時所返回的重新整理令牌。重新整理令牌可以用於重新整理過期的訪問令牌,無需資源所有者的存在就可以再次進行身份驗證。客戶端需要明確地向伺服器請求重新整理令牌。
  • redirect_uri:授權伺服器將使用者代理作為授權碼請求的一部分重定向到該 URI。授權碼被髮送到這個 URI。
  • authentication_server_url:這表示授權伺服器。獲取授權碼的所有請求都需要被髮送到這個 URL。
  • token_endpoint_url:這表示令牌端點。通過重新整理令牌請求來獲取訪問令牌和重新整理訪問令牌的所有請求都需要傳送到這個 URL。
  • resource_server_url:這表示需要聯絡的資源伺服器的 URL,通過將 Authorization 標頭中的訪問令牌傳遞給它,訪問受保護的資源。
  • approval_prompt_key:授權伺服器用於定義授權提示條件的屬性名。通常,每個授權伺服器(Salesforce、Google、IBM 等)都有一個需要作為授權碼請求的一部分傳遞的自定義屬性,用於表示該客戶端是否要強制執行每個請求的授權提示。Google 的屬性名是approval_prompt。對於 Salesforce,它是 login consent
  • access_type_key:授權伺服器用於定義訪問型別條件的屬性名。通常,每個授權伺服器(Salesforce、Google、IBM 等)都有一個自定義方法,客戶端可以通過它傳達指令,比如它想要一個重新整理令牌和訪問令牌作為訪問令牌請求的一部分。Google 通過提供 access_type 屬性執行此方法。Salesforce 要求您輸入 refresh_token 值作為 scope 的一部分。
  • access_type_valueaccess_type_key 屬性的值。對於 Google,您需要將 offline 值傳遞給伺服器,以包含重新整理令牌和訪問令牌。

圖 2 顯示了示例客戶端程式碼的 index.html 頁面。在 Eclipse 中成功配置專案並將其部署到 Tomcat 後,您應該看到這個頁面。

圖 2. 測試客戶端首頁

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

客戶端暴露了您需要了解的有關 OAuth 2.0 的兩種最基本操作。首先,客戶端顯示瞭如何從伺服器獲取一個訪問令牌。其次,客戶端顯示瞭如何使用現有的訪問令牌訪問來自伺服器的受保護資源。

執行客戶端:

  • 首先,在 Oauth2Client.config 檔案中輸入所有所需的值。
  • 單擊 Start Test 來獲取訪問令牌。一個 HTTP GET 請求被髮送到 OAuth2Client servlet,可以在以下 URI 訪問它,http://localhost:8080/OAuth2.0_AuthCode/handler。
  • 使用者介面程式碼傳遞下面的查詢引數,作為呼叫 OAuth2Client servlet 的一部分: caller=token (access token request), caller=resource (access protected resource request)

清單 6 中的客戶端程式碼段選自 OAuth2Client 類的 doGet 方法。

清單 6. 節選自示例客戶端的 doGet 方法
String caller = request.getParameter(OAuthConstants.CALLER);
String code = request.getParameter(OAuthConstants.CODE);

//Load the properties file
Properties config = OAuthUtils.getClientConfigProps(OAuthConstants.CONFIG_FILE_PATH);

//Generate the OAuthDetails bean from the config properties file
OAuth2Details oauthDetails = OAuthUtils.createOAuthDetails(config);

//Validate Input
List<String> invalidProps = OAuthUtils.validateInput(oauthDetails);
	if(invalidProps!=null && invalidProps.size() == 0){
		//Validation successful

		if(OAuthUtils.isValid(caller)){
			//Request was sent from web application.
			//Check type of request
			if(caller.equalsIgnoreCase(OAuthConstants.TOKEN)){
				//Request for Access token
				oauthDetails.setAccessTokenRequest(true);
				String location = 			 		
				OAuthUtils.getAuthorizationCode(oauthDetails);

				//Send redirect to location returned by endpoint
				response.sendRedirect(location);
				return;
		}
		else{
			//Request for accessing protected resource
				if(!OAuthUtils.isValid(oauthDetails.getResourceServerUrl())){
					invalidProps.add(OAuthConstants.RESOURCE_SERVER_URL);

				}

				if(!OAuthUtils.isValid(oauthDetails.getAccessToken())){
					if(!OAuthUtils.isValid(oauthDetails.getRefreshToken())){
						invalidProps.add(OAuthConstants.REFRESH_TOKEN);
					}

				}

				if(invalidProps.size() > 0){
					sendError(response, invalidProps);
					return;
				}

				Map<String,String> map = OAuthUtils.getProtectedResource(oauthDetails);
				response.getWriter().println(new Gson().toJson(map));
				return;
			}
		}
		else if(OAuthUtils.isValid(code)){
			//Callback from endpoint with code.
			Map<String,String> map = OAuthUtils.getAccessToken(oauthDetails, code);
			response.getWriter().println(new Gson().toJson(map));
			return;
		}
		else{
			//Invalid request/error response
			String queryString = request.getQueryString();
			String error = "Invalid request";
			if(OAuthUtils.isValid(queryString)){
				//Endpoint returned an error
				error = queryString;
			}
			response.getWriter().println(error);
			return;

			}
		}
		else{
			//Input is not valid.Send error
			sendError(response, invalidProps);
			return;

		}

清單 6 的注意事項:

  • 客戶端檢索查詢引數 caller 和 code。如前面所示,由使用者介面傳送該請求, caller 引數會有一個有效的值;否則,授權伺服器傳送該請求作為重定向呼叫的一部分,而且 code 會有一個有效的值。
  • 然後,客戶端通過讀取 OAuth2Client.config 檔案中提供的屬性,建立一個 OAuthDetails bean。
  • 然後,驗證此 bean 的正確性和完整性。如果發現任何無效屬性或缺失的屬性,相應於缺失/不正確的屬性的錯誤響應被髮送到使用者介面。
  • 然後,客戶端程式碼繼續驗證所請求的操作,並呼叫相應的操作。

訪問令牌請求

清單 7 中的程式碼演示瞭如何發出授權程式碼請求。

清單 7. 授權碼請求程式碼

點選檢視程式碼清單

有關 清單 7 中的程式碼的注意事項:

  • 程式碼在一開始就建立一個 HttpPost 方法,它用於傳送 URL 編碼的引數。
  • response_typeclient_id 和 redirect_uri 是強制性引數,被包括在請求中。
  • 其他可選引數,比如 statescopeapproval_prompt_key/value 和 access_type_key/value,如果配置檔案中提供了它們的有效值,則將它們也包括在內。
  • 使用 DefaultHttpClient 向授權伺服器發出請求。
  • 授權伺服器驗證請求引數,並回復 302 HTTP 重定向程式碼與 Location 標頭。
  • 程式碼識別從授權伺服器收到的 302,並從響應標頭中檢索 Location 標頭。
  • Location 標頭包含客戶端重定向使用者代理(Web 瀏覽器)所需的 URI。該 URI 通常是一個登入提示,讓資源所有者登入並向客戶端提供授權。
  • 位置 URI 的值被返回給呼叫方法 (OAuth2Client.doGet())。
  • Oauth2Client.doGet() 方法將響應重定向到位置 URI。
  • 資源所有者的使用者代理(Web 瀏覽器)現在被重定向到登入頁面/授權頁面,資源所有者需要登入此頁面,並向發出請求的客戶端提供授權。
  • 資源所有者為客戶端提供授權後,授權伺服器將會使用在原始授權碼請求中傳遞的重定向 URI 來傳送 code

清單 8 中的程式碼顯示了使用在上一步中接收到的 code 來發出最終的訪問令牌請求。

清單 8. 示例程式碼訪問令牌請求
HttpPost post = new HttpPost(oauthDetails.getTokenEndpointUrl());
		String clientId = oauthDetails.getClientId();
		String clientSecret = oauthDetails.getClientSecret();
		String scope = oauthDetails.getScope();
		Map<String, String> map = new HashMap<String, String>();

		List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>();

		parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE,
				oauthDetails.getGrantType()));

		parametersBody.add(new BasicNameValuePair(OAuthConstants.CODE,
				authorizationCode));

		parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID,
				clientId));

		if (isValid(clientSecret)) {
			parametersBody.add(new BasicNameValuePair(
					OAuthConstants.CLIENT_SECRET, clientSecret));
		}

		parametersBody.add(new BasicNameValuePair(OAuthConstants.REDIRECT_URI,
				oauthDetails.getRedirectURI()));

		DefaultHttpClient client = new DefaultHttpClient();
		HttpResponse response = null;
		String accessToken = null;
		try {
			post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8));

			response = client.execute(post);
			int code = response.getStatusLine().getStatusCode();

			map = handleResponse(response);
			accessToken = map.get(OAuthConstants.ACCESS_TOKEN);

		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}

		return map;

清單 8 的注意事項:

  • 該程式碼使用 HttpPost 方法向令牌端點發出訪問令牌請求。
  • grant_typecodeclient_id 和 redirect_uri 是強制性引數。
  • 如果 client_secret 是有效的,那麼它也會包含在請求中。
  • code 的值是先前的請求中由授權伺服器返回的程式碼。
  • 如果請求是有效的,那麼令牌端點將會返回訪問令牌,如果請求是離線訪問,則會返回重新整理令牌。
  • 請注意,作為該請求的一部分傳送的重定向 URI 需要與作為授權碼請求的一部分傳送的重定向 URI 完全相同。令牌端點需要驗證該重定向 URI 與客戶端的應用程式中所指定的 URI 相匹配,其資料由令牌端點提供。

重新整理訪問令牌

如前所述,訪問令牌通常是臨時性的,典型的壽命是一個小時。重新整理令牌允許客戶端自動重新整理過期的訪問令牌,無需資源所有者登入,並再次對客戶端進行身份驗證。清單 9 中的程式碼說明了這一點。

清單 9. 重新整理訪問令牌的示例程式碼
String clientId = oauthDetails.getClientId();
String clientSecret = oauthDetails.getClientSecret();
String scope = oauthDetails.getScope();
String refreshToken = oauthDetails.getRefreshToken();
Map<String, String> map = new HashMap<String, String>();

		if (!isValid(refreshToken)) {
			throw new RuntimeException(
					"Please provide valid refresh token in config file");
		}

		List<BasicNameValuePair> parametersBody = new ArrayList<BasicNameValuePair>();

		parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE,
				OAuthConstants.REFRESH_TOKEN));

		parametersBody.add(new BasicNameValuePair(OAuthConstants.REFRESH_TOKEN,
				oauthDetails.getRefreshToken()));

		if (isValid(clientId)) {
			parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID,
					clientId));
		}

		if (isValid(clientSecret)) {
			parametersBody.add(new BasicNameValuePair(
					OAuthConstants.CLIENT_SECRET, clientSecret));
		}

		if (isValid(scope)) {
			parametersBody.add(new BasicNameValuePair(OAuthConstants.SCOPE,
					scope));
		}

		DefaultHttpClient client = new DefaultHttpClient();
		HttpResponse response = null;
		try {
			post.setEntity(new UrlEncodedFormEntity(parametersBody, HTTP.UTF_8));

			response = client.execute(post);
			int code = response.getStatusLine().getStatusCode();

			map = handleResponse(response);

		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}

		return map;

清單 9 的注意事項:

  • 該程式碼使用 HttpPost 方法向令牌端點發出請求。
  • grant_type 和 refresh_token 引數是強制性的。
  • 如果有效,client_idclient_secret 和 scope 也被包括在內。
  • 如果請求有效,令牌端點會返回一個新的訪問令牌。

訪問受保護資源

清單 10 中的程式碼演示瞭如何使用訪問令牌來訪問受保護的資源。如果訪問令牌過期,則重新整理訪問令牌,然後重試訪問受保護的資源。

清單 10. 訪問受保護資源的示例程式碼

點選檢視程式碼清單

清單 10 的注意事項:

  • 此方法使用從配置檔案檢索到的值來接受 OauthDetails bean。
  • 顧名思義,這種方法將嘗試使用簡單的 HttpGet 從資源伺服器中檢索受保護的資源。
  • 為了向資源伺服器進行身份驗證,需要將訪問令牌作為 Authorization 標頭的一部分進行傳送。例如,Authorization:Bearer accessTokenValue
  • 該程式碼建立了一個 DefaultHttpClient,向資源伺服器發出一個 get 請求。
  • 如果從資源伺服器收到的響應程式碼是 401 或 403,那麼用於身份驗證的訪問令牌可能已過期或無效。
  • 下一步是重新建立訪問令牌(請參閱 清單 9)。
  • 成功重新生成訪問令牌之後,該程式碼將會更新 OauthDetails bean 中的訪問令牌值。該程式碼還會使用新的訪問令牌值替換 get 方法中現有的 Authorization 標頭。
  • 該程式碼現在發出對受保護資源的另一個訪問請求。
  • 如果訪問令牌有效,而且資源伺服器的 URL 也是正確的,那麼您應該可以在控制檯中看到響應內容。

 

用 Salesforce.com 測試客戶端

在 Salesforce.com 註冊

Salesforce.com 是一個流行的軟體即服務 (SaaS) 應用程式,支援 OAuth 2.0 的認證碼授權型別。

在 Salseforce.com 註冊:

  1. 單擊 Login,然後單擊 Sign up for free
  2. 完成註冊,獲得您的憑據。
  3. 除了使用者名稱和密碼以外,您還會收到一個安全令牌。您為了發出訪問令牌請求而提供的密碼必須是您的密碼和安全令牌的串聯(例如,password12312123)。
  4. 請參閱 參考資料,從其中一篇有用的文章連結中瞭解如何在 salesforce.com 中建立一個應用程式。
  5. salesforce.com 使用您在應用程式建立過程中指定的重定向 URI 與您的認證碼授權型別進行通訊。

用 Salesforce.com 執行示例客戶端

現在,您已經在 Salesforce 設定了 OAuth 2.0 相容的伺服器,您可以測試客戶端並從伺服器檢索受保護的資訊。

您可以在 Java resources/src 資料夾下找到 Oauth2Client_salesforce.config 檔案。這個配置檔案是為 salesforce.com 定製的,可以用作一個提供配置值的模板,以便針對 Salesforce.com 進行測試。

如上文所示,在 Tomcat 伺服器上執行 Oauth2Client Web 應用程式,進入測試客戶端的開始頁面,如 圖 3 所示。

圖 3. Salesforce 測試客戶端頁面

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

圖 3 顯示了測試客戶端頁面。單擊 Start Test 獲取訪問令牌。接下來,您會看到 Salesforce 訪問令牌請求的登入提示,如 圖 4 所示。

圖 4. Salesforce 訪問令牌請求(登入提示)

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

圖 4 顯示了 Salesforce 登入頁面,從 Salesforce.com 收到 302 和 Location 標頭後,客戶端將使用者代理(Web瀏覽器)重定向到該頁面。

在登入後,您會看到 Salesforce 訪問令牌請求批准提示,如 圖 5 所示。

圖 5. Salesforce 訪問令牌請求(批准提示)

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

圖 5 顯示資源所有者的批准提示(登入後),向 VarunOAuth2.0 App 授予訪問許可權,該應用是請求訪問資源所有者的資料的示例程式碼。

Salesforce 訪問令牌輸出

在 Salesforce 批准提示上對客戶端授予訪問許可權之後,令牌端點的響應中包括訪問令牌和重新整理令牌,以及其他 Salesforce 特定的資料。您應該在 Eclipse 控制檯視窗中看到輸出,如 清單 11 所示。

清單 11. Salesforce 訪問令牌輸出

點選檢視程式碼清單

從 Salesforce 伺服器檢索使用者資訊

現在,您有了訪問令牌,可以發出請求,從 Salesforce 訪問資源所有者的帳戶資訊,該伺服器要求使用 OAuth 2.0 進行身份驗證。

用訪問令牌和重新整理令牌更新 Oauth2Client.confg 檔案。還要使用您想測試的資源伺服器 URL 來設定資源伺服器 URL 屬性。例如,您可以將其設定為由 Salesforce 作為訪問令牌響應的一部分返回的 id 欄位。這個 id 欄位用於訪問資源所有者的帳戶資訊(例如,https://login.salesforce.com/id/00D90000000mQaYEAU/00590000001HCB7AAO)。

重新整理該專案,並在 Tomcat 伺服器上再次執行該專案。單擊 Start Test 來訪問受保護的資源。

您的 Eclipse 控制檯視窗應該顯示類似於 清單 12 的輸出。

清單 12. Salesforce 保護的資源輸出

點選檢視程式碼清單

如您所見,您可以通過使用 OAuth 2.0 進行身份驗證,從 Salesforce 成功訪問資源所有者的帳戶資訊。在配置檔案中提供的訪問令牌過期後,客戶端會在下一個請求中自動重新生成訪問令牌,並使用它來檢索資源伺服器 URL 中提供的受保護資源。

在 Google 註冊

Google 使用 OAuth 2.0 對 API 進行身份驗證,這些 API 可以用於訪問如 GoogleDrive、TaskQueue 和 CloudSQL 等服務。按照以下網址的說明,您可以設定一個應用程式,在 Google 上測試您的 OAuth 2.0 客戶端:https://developers.google.com/accounts/docs/OAuth2?hl=ES

用 Google 執行客戶端

現在,您已經設定了 OAuth 2.0 相容的伺服器,您可以測試客戶端,並從伺服器檢索受保護的資訊。

您可以在 Java resources/src 資料夾下找到 Oauth2Client_Google.config 檔案。這個配置檔案被定製為與 Google 服務結合使用,而且可以用作提供配置值的模板。

在 Tomcat 伺服器上執行 Oauth2Client Web 應用,如前所示。獲取訪問令牌操作和訪問受保護資源操作的流程和輸出如 圖 6 所示。

圖 6. 訪問令牌請求(登入提示)

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

圖 6 顯示了 Google 登入頁面,從 Google 收到 302 和 Location 標頭後,客戶端將使用者代理(Web瀏覽器)重定向到該頁面。

圖 7 顯示了資源所有者的批准提示(登入後),向 Cast Iron App 授予訪問許可權,該應用請求訪問資源所有者的資料。

圖 7. 訪問令牌請求(批准提示)

Java 程式設計中的 OAuth 2.0 客戶端,第 3 部分: 認證碼授權

資源所有者對客戶端授予訪問許可權後,令牌端點的響應中包含訪問令牌和重新整理令牌。您應該在控制檯視窗中看到類似於 清單 13 所示的輸出。

清單 13.
********** Response Received **********
  expires_in = 3600
  token_type = Bearer
  refresh_token = 1/TtCxaFlKMRsHeIlxrY-2ZJIO8DcRmQEiQ_2Wxw8
  access_token = ya29.ZQDpI-ahF6TMURwAAABqBu-2-U0_lUWfbwh053j3db3PzaNXV4k_k6fc_VT7uQ

從 Google 伺服器檢索使用者資訊

現在,您有了訪問令牌,可以發出請求,訪問 GoogleDrive 資訊,這要求使用 OAuth 2.0 進行身份驗證。

用訪問令牌和重新整理令牌更新 Oauth2Client.confg 檔案,並使用您希望對其進行測試的資源伺服器 URL 來填充資源伺服器 url 屬性。我要用 https://www.googleapis.com/drive/v2/about 替換它,這是 GoogleDrive 帳戶資訊 URL。

現在,重新整理該專案,並在 Tomcat 伺服器上再次執行它。單擊 Start Test 來訪問受保護的資源。

您應該在控制檯視窗中看到類似於 清單 14 的片段。

清單 14.

點選檢視程式碼清單

如您所見,您可以通過使用 OAuth 2.0 進行身份驗證,從 GoogleDrive 成功訪問資源所有者的帳戶資訊。

在配置檔案中提供的訪問令牌過期後,客戶端會在下一個請求中自動重新生成訪問令牌,並使用重新生成的訪問令牌來檢索資源伺服器 URL 中提供的受保護資源。

用 IBM 端點來測試客戶端

客戶端示例程式碼已經用 OAuth 2.0 相容的 IBM 端點(比如 IBM Websphere® Application Server 和 IBM DataPower)成功完成了測試。®

關於在 Websphere Application Server 上設定 OAuth 2.0 伺服器的說明,請參閱 “使用 OAuth:在 WebSphere Application Server 上啟用 OAuth 服務提供程式“。

可以採用與在 Salesforce 和 Google 上進行測試的相同方法對 IBM 端點進行測試。

結束語

本教程系列的第 3 部分解釋了認證碼授權型別的基礎知識。本文演示瞭如何在 Java 程式碼中編寫一個通用的 OAuth 2.0 客戶端,以便連線到 OAuth 2.0 相容的多個端點,從中獲取受保護的資源。來自 下載 的示例客戶端 Web 專案可以匯入到 Eclipse 環境中,您可以將此作為一個起點,建立一個定製的 Oauth 2.0 客戶端。

下載

描述 名字 大小
Sample Java code for Authorization code grant type OAuth2.0_AuthCode_part_3.war 25KB

相關文章