软件授权注册码_授权码授予

软件授权注册码

OAuth是一种开放的授权标准,可让客户端代表资源所有者访问受保护的服务器资源。 资源所有者可以是其他客户端或最终用户。 OAuth还可以帮助最终用户授权第三方访问其服务器资源,而无需共享其凭据(例如用户名和密码)。 本系列文章遵循RFC6749中概述的OAuth 2.0授权框架。 可以在Internet工程任务组网站上找到RFC 6749中概述的完整OAuth 2.0授权框架。

授权补助

授权授予是一个凭证,代表可用于访问受保护资源的资源所有者的授权。 客户端使用此凭据来获取访问令牌,并且最终将该访问令牌与访问受保护资源的请求一起发送。 OAuth 2.0定义了四种授权类型:

  1. 授权码
  2. 隐含的
  3. 资源所有者密码凭证
  4. 客户凭证

本系列文章将指导您使用上面列出的每种授予类型,在Java™编程中实现OAuth 2.0客户端。 在第三部分中,我将解释如何实现授权码授予。 本文将详细说明该赠款,并说明可用于与支持此赠款的任何OAuth 2.0兼容服务器进行交互的示例客户端代码。 到本文结尾,您应该对客户端实现有完整的了解,并准备下载示例客户端代码以进行自己的测试。

授权码授予

该授权针对机密客户端进行了优化,用于获取访问令牌和刷新令牌。 这是基于重定向的流程,因此,客户端必须能够与资源所有者的用户代理(通常是Web浏览器)进行交互,并且还必须能够(通过重定向)接受来自授权服务器的传入请求。

授权代码授予如图1所示。

图1.授权代码流程
流程图显示授权码流程

图1所示的流程包括以下步骤:

  • (A)客户端(通常是Web应用程序)通过将资源所有者的用户代理(通常是Web浏览器)定向到授权端点来启动流程。 客户端的请求包括客户端标识符,请求的范围,本地状态和重定向URI。 授权访问(或拒绝访问)后,授权服务器将用户代理(通常是Web浏览器)定向回重定向URI。
  • (B)资源所有者通过用户代理向授权服务器进行身份验证,并授予或拒绝客户端的访问请求。
  • (C)如果资源所有者授予访问权限,则授权服务器使用之前(在请求中或在客户端注册期间)提供的重定向URI将用户代理(通常是Web浏览器)重定向回客户端。 重定向URI包括授权码和客户端之前提供的任何本地状态。
  • (D)客户端通过包括上一步中收到的授权代码,从授权服务器的令牌端点发出访问令牌请求。 发出请求时,客户端使用客户端凭据向授权服务器进行身份验证。 客户端还包括用于获得授权码以进行验证的重定向URI。
  • (E)授权服务器对客户端进行身份验证。 它验证授权码,并确保接收到的重定向URI与步骤(C)中用于重定向客户端的URI匹配。 如果有效,授权服务器将以访问令牌和(可选)刷新令牌进行响应,以防请求离线访问。

授权码请求

授权代码请求对应于图1中描述的步骤(A)和(B)。在步骤(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,并使用application/x-www-form-urlencoded将授权代码包括为重定向URI的查询组件的一部分。格式。

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是否有效。 对于机密客户端,授权服务器还使用在请求正文或授权标头中传递的客户端凭据对客户端进行身份验证。

访问令牌响应

这对应于图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项目。 您可以从Downloads下载包含项目和源代码的.war文件。 您将.war文件导入到Eclipse环境中。

先决条件

您需要Java EE开发人员的Eclipse IDE来设置开发环境并导入附加的项目。 您可以从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

可以在HttpComponents JAR文件中找到第1至6点中提到的JAR文件,可以从HttpComponents Downloads下载页面下载该文件 。 可以从JSON Simple项目下载json-simple-1.1.1.jar文件。 确保将这些jar复制到Apache Tomcat安装目录的lib文件夹中。 默认情况下,某些必需的JAR文件在Tomcat中可能已经可用,因此您只需要复制丢失的文件。

将项目.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 AsRun 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 :这是一个可选参数。 它表示访问请求的范围。 服务器返回的访问令牌只能访问范围中提到的那些服务。
  • 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。
  • approval_prompt_key :授权服务器用来定义批准提示条件的属性名称。 通常,每个授权服务器(Salesforce,Google,IBM等)都具有一个自定义属性,该属性需要作为授权代码请求的一部分传入,以表明客户端是否要针对每个请求强制执行批准提示。 Google的属性名称为approval_prompt 。 对于Salesforce,这是login consent
  • access_type_key :授权服务器用来定义访问类型条件的属性名称。 通常,每个授权服务器(Salesforce,Google,IBM等)都有一个自定义方法,客户端可以通过该方法传达其希望与访问令牌一起发布刷新令牌的信息,作为访问令牌请求的一部分。 Google通过提供access_type属性来实现。 Salesforce要求您输入值refresh_token作为范围的一部分。
  • access_type_valueaccess_type_key属性的值。 对于Google,您需要offline传递值,以便服务器在访问令牌之外还包括刷新令牌。

图2显示了示例客户端代码的index.html页面。 在Eclipse中成功配置项目并将其部署到Tomcat后,应该会看到此页面。

图2.测试客户端主页
OAuth 2.0索引页面的屏幕图像

客户端提供了您需要了解的有关OAuth 2.0的两个基本操作。 首先,客户端显示如何从服务器获取访问令牌。 其次,客户端展示了如何使用现有的访问令牌从服务器访问受保护的资源。

要运行客户端:

  • 首先,将所有必需的值放在Oauth2Client.config文件中。
  • 单击开始测试获取访问令牌。 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的注释:

  • 客户端检索查询参数, callercode 。 如果请求是通过用户界面发送的(如前所示),则caller参数将具有有效值; 否则,该请求是作为重定向调用的一部分从授权服务器发送的,并且code将具有有效值。
  • 然后,客户端代码通过读取OAuth2Client.config文件中提供的属性来创建OAuthDetails bean。
  • 然后验证Bean的正确性和完整性。 如果找到任何无效或缺少的属性,则会将具有缺少/不正确的属性的适当错误响应发送到用户界面。
  • 然后,客户端代码继续验证请求的操作,并调用适当的操作。

访问令牌请求

清单7中的代码演示了如何发出授权代码请求。

清单7.授权代码请求代码
HttpPost post = new HttpPost(oauthDetails.getAuthenticationServerUrl());
String location = null;

String state = oauthDetails.getState();

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

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

parametersBody.add(new BasicNameValuePair(OAuthConstants.CLIENT_ID,
		oauthDetails.getClientId()));

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

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

if (isValid(oauthDetails.getApprovalPromptValue())) {
	parametersBody.add(new BasicNameValuePair(
			oauthDetails.getApprovalPromptKey(), oauthDetails
					.getApprovalPromptValue()));
}
		
if (isValid(oauthDetails.getAccessTypeValue())) {
	parametersBody.add(new BasicNameValuePair(
			oauthDetails.getAccessTypeKey(), oauthDetails
					.getAccessTypeValue()));
}

if (isValid(state)) {
	parametersBody.add(new BasicNameValuePair(OAuthConstants.STATE,
			oauthDetails.getState()));
}

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();
	System.out.println("Code " + code);
	Map<String, String> map = handleURLEncodedResponse(response);

	if (OAuthConstants.HTTP_SEND_REDIRECT == code) {
		location = getHeader(response.getAllHeaders(),
				OAuthConstants.LOCATION_HEADER);
		if (location == null) {
			System.out
					.println("The endpoint did not pass in valid location header for redirect");
			throw new RuntimeException(
					"The endpoint did not pass in valid location header for redirect");
		}
	} else {
		System.out
				.println("Was expecting code 302 from endpoint to indicate redirect. Recieved httpCode "
						+ code);
		throw new RuntimeException(
				"Was expecting code 302 from endpoint to indicate redirect. Recieved httpCode "
						+ code);
	}

} 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 location;

清单7中的代码说明:

  • 该代码从创建HttpPost方法开始,该方法用于发送URL编码的参数。
  • response_typeclient_idredirect_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中的代码显示了如何使用上一步中收到的代码进行最终访问令牌请求。

清单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_idredirect_urigrant_type参数。
  • 如果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_typerefresh_token参数是必需的。
  • 如果有效,则client_id包括client_idclient_secretscope
  • 如果请求有效,则令牌端点返回一个新的访问令牌。

访问受保护的资源

清单10中的代码演示了如何使用访问令牌访问受保护的资源。 如果访问令牌已过期,则代码将刷新访问令牌,然后重试访问受保护的资源。

清单10.访问受保护资源的示例代码
String resourceURL = oauthDetails.getResourceServerUrl();

		Map<String, String> map = new HashMap<String, String>();

		HttpGet get = new HttpGet(resourceURL);
		get.addHeader(OAuthConstants.AUTHORIZATION,
				getAuthorizationHeaderForAccessToken(oauthDetails
						.getAccessToken()));
		DefaultHttpClient client = new DefaultHttpClient();
		HttpResponse response = null;
		String accessToken = null;
		int code = -1;
		try {
			response = client.execute(get);
			code = response.getStatusLine().getStatusCode();
			if (code == OAuthConstants.HTTP_UNAUTHORIZED
					|| code == OAuthConstants.HTTP_FORBIDDEN) {
				// Access token is invalid or expired.Regenerate the access
				// token
				System.out
						.println("Access token is invalid or expired. Refreshing access token....");
				map = refreshAccessToken(oauthDetails);
				accessToken = map.get(OAuthConstants.ACCESS_TOKEN);

				if (isValid(accessToken)) {
					// update the access token
					System.out.println("New access token: " + accessToken);
					oauthDetails.setAccessToken(accessToken);
					get.removeHeaders(OAuthConstants.AUTHORIZATION);
					get.addHeader(OAuthConstants.AUTHORIZATION,
							getAuthorizationHeaderForAccessToken(oauthDetails
									.getAccessToken()));
					get.releaseConnection();
					response = client.execute(get);
					code = response.getStatusLine().getStatusCode();
					if (code >= 400) {
						throw new RuntimeException(
								"Could not access protected resource. Server returned http code: "
										+ code);

					}

				} else {
					throw new RuntimeException("Could not refresh access token");
				}

			}

			map = handleResponse(response);

		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			get.releaseConnection();
		}

		return map;

关于清单10的注释:

  • 此方法接受OauthDetails bean以及从配置文件中检索到的值。
  • 顾名思义,此方法使用简单的HttpGet方法来尝试从资源服务器检索受保护的资源。
  • 要向资源服务器进行身份验证,需要将访问令牌作为授权标头的一部分发送。 例如, Authorization: Bearer accessTokenValue
  • 该代码创建一个DefaultHttpClient来向资源服务器发出获取请求。
  • 如果从资源服务器收到的响应代码是401或403,则用于身份验证的访问令牌可能已过期或无效。
  • 下一步是重新生成访问令牌(请参见清单9)。
  • 成功重新生成访问令牌之后,代码将更新OauthDetails bean中的访问令牌值。 该代码还将get方法中的现有授权标头替换为新的访问令牌值。
  • 该代码现在再次发出访问受保护资源的请求。
  • 如果访问令牌有效且资源服务器URL正确,则您应该能够在控制台中看到响应的内容。

使用Salesforce.com测试客户端

在Salesforce.com上注册

Salesforce.com是流行的软件即服务(SaaS)应用程序,支持OAuth 2.0的授权码授予类型。

要在Salseforce.com上注册:

  1. 单击Login ,然后单击免费注册
  2. 完成注册以获取您的凭据。
  3. 除了用户名和密码,您还会收到安全令牌。 您提供的用于发出访问令牌请求的密码必须是您的密码和安全令牌的串联(例如password12312123 )。
  4. 您在应用创建过程中指定的重定向URI被salesforce.com用于与您通信以获取授权码授予类型。

使用Salesforce.com运行示例客户端

现在,您已经在Salesforce上设置了符合OAuth 2.0的服务器,现在可以测试客户端并从服务器检索受保护的信息了。

您可以在Java resources / src文件夹下找到Oauth2Client_salesforce.config文件。 此配置文件是为salesforce.com定制的,可以用作提供配置值以针对Salesforce.com进行测试的模板。

如前所述,在Tomcat服务器上运行Oauth2Client Web应用程序,以进入Test client start页面,如图3所示。

图3. Salesforce测试客户端页面
测试客户端起始页

图3显示了测试客户端页面。 单击开始测试获取访问令牌。 接下来,您将看到关于Salesforce访问令牌请求的登录提示,如图4所示。

图4. Salesforce访问令牌请求(登录提示)
Salesforce.com登录窗口

图4显示了从Salesforce.com收到302和Location标头后,客户端将用户代理(Web浏览器)重定向到的Salesforce登录页面。

登录后,您将看到Salesforce访问令牌请求批准提示,如图5所示。

图5. Salesforce访问令牌请求(批准提示)
访问请求画面

图5显示了资源所有者的批准提示(登录后),以授予对VarunOAuth2.0 App的访问权限,这是请求访问资源所有者的数据的示例代码。

Salesforce访问令牌输出

在Salesforce批准提示上授予对客户端的访问权限后,令牌端点将以访问令牌和刷新令牌以及其他特定于Salesforce的数据进行响应。 您应该在Eclipse控制台窗口中看到输出,如清单11所示。

清单11. Salesforce访问令牌输出
********** Response Received **********
  id = https://login.salesforce.com/id/00D90000000mQaYEAU/00590000001HCB7AAO
  issued_at = 1408356835704
  scope = full refresh_token
  instance_url = https://ap1.salesforce.com
  token_type = Bearer
  refresh_token = 5Aep8617VFpoP.M.4vPT_5eQXhIJTvFPNyK2GaBz7xFooRQE590MJSZNVqfTXKUqoiZH_yhm_ZpaVsmp
  id_token = eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjE5MCJ9.eyJleHAiOjE0MDgzNTY5NTUsInN1YiI6Imh0dHBzOi8vbG9naW4uc2FsZXNmb3JjZS5jb20vaWQvMDBEOTAwMDAwMDBtUWFZRUFVLzAwNTkwMDAwMDAxSENCN0FBTyIsImF0X2hhc2giOiJ3eWJpZ1NRTEtaNjdiYlRxWUJxNEVnIiwiYXVkIjoiM01WRzlZNmRfQnRwNHhwNXhLcHgxcFBtcFZidnlUV09Gd2FWbkxwOFpDYjRZVjBRX2FJcHI2WTZVazd1ZHcxcWtCTFZQcVVFNWY2blU1NW5xTWljMCIsImlzcyI6Imh0dHBzOi8vbG9naW4uc2FsZXNmb3JjZS5jb20iLCJpYXQiOjE0MDgzNTY4MzV9.W-r2-k-creIijmRmZ5qQff-bonjjtynNivJAv5XaSGedGhPavWlQpmn76B9k5vB7TWUDTX6y2NroTuIpBi-F2jrO0dunN1giPfv-YKyrCYrpy75J7NXmnSnDGrKhhYgkcR7x9s9MLMoMD-Vf1wsJr58XU2He-UwfrihfkdJzvLKiWRNQfz1gCUwlNSws70AQSqrBfB6MHpLqE7ogG1M3SOp6B4hbcA8NC_zwnvJwIDmF4t3_rf6VsgPuPjV_t4PphBhbrln7sm4b9OMcRRycc8WcCvgBvNPjI37uImciDYUGIP25NEy5sRM3mEU392YlmR5AoHUqOVOqdO9DS1ULWxBy3Q-Fp1wyKYyWiCrUMXe5QdtmeBmkpzptCKXwAhfxhLBdai4bBFfh8K3If4UP-WeNcpMwNkiRhVElwntlqtCPaSs4BLiZGonpLLgwET-f_Iyxs4BauCNvyWDbme_2it2V5AEHgJoKvuf2oU6hD8-Sit8MsdEdc2ugf-nk96QJt5px3QvChfDIE8B7W5trzXagkvzVcXXJV06zJbDUf-ioz7zDTI4Popkxlb31cQiaLAtz2IxIUtjZAfAcTXJaxi8txjV8glqS8Z61145bUaitXgGmZhZAqeefrqLneyCDD--EPNWDIdQYSPhRPbtFb5Aa89AMDNIePxTNnIWShNs
  access_token = 00D90000000mQaY!AQ8AQMgSI4eZOjvKJGVPuWISCkuS1WC0R6gOWpMg57bMwVWGkyS9_Wraa5ooMrjTfaqMmXmXlgParPk1rjn0vH5BxbNRq3W0
  signature = o1VBoC/PzvMw08hZGxYvXLiV/SXQirHBl8qOrk1Mu6Q=

从Salesforce服务器检索用户信息

有了访问令牌后,您就可以从Salesforce提出访问资源所有者的帐户信息的请求,这需要OAuth 2.0身份验证。

使用访问令牌更新Oauth2Client.confg文件并刷新令牌。 还使用您要测试的资源服务器URL设置资源服务器URL属性。 例如,您可以将其设置为Salesforce返回的id字段,作为访问令牌响应的一部分。 此id字段用于访问资源所有者的帐户信息(例如, https://login.salesforce.com/id/00D90000000mQaYEAU/00590000001HCB7AAO )。

刷新项目,然后在Tomcat服务器上再次运行该项目。 单击开始测试访问受保护的资源。

您的Eclipse控制台窗口应显示类似于清单12的输出。

清单12.受Salesforce保护的资源输出
********** Response Received **********
  addr_city = null
  email_verified = true
  locale = en_US
  addr_state = Unknown
  asserted_user = true
  nick_name = vern.ojha1********
  id = https://login.salesforce.com/id/00D9000000QaYEAU/005900001HCB7AAO
  first_name = varun
  timezone = America/Los_Angeles
  username = *******
  mobile_phone = null
  user_id = ************
  addr_street = null
  status = {"created_date":null,"body":null}
  user_type = STANDARD
  urls = {"sobjects":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/sobjects\/","feeds":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/chatter\/feeds","users":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/chatter\/users","query":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/query\/","enterprise":"https:\/\/ap1.salesforce.com\/services\/Soap\/c\/{version}\/00D90000000mQaY","recent":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/recent\/","feed_items":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/chatter\/feed-items","search":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/search\/","partner":"https:\/\/ap1.salesforce.com\/services\/Soap\/u\/{version}\/00D90000000mQaY","rest":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/","groups":"https:\/\/ap1.salesforce.com\/services\/data\/v{version}\/chatter\/groups","metadata":"https:\/\/ap1.salesforce.com\/services\/Soap\/m\/{version}\/00D90000000mQaY","profile":"https:\/\/ap1.salesforce.com\/0000001CB7AAO"}
  mobile_phone_verified = false
  is_app_installed = true
  photos = {"picture":"https:\/\/c.ap1.content.force.com\/profilephoto\/005\/F","thumbnail":"https:\/\/c.ap1.content.force.com\/profilephoto\/005\/T"}
  display_name = varun ojha
  last_modified_date = 2013-06-04T07:43:42.000+0000
  email = **************
  addr_country = IN
  organization_id = 00D90000000mQaYEAU
  last_name = ojha
  utcOffset = -28800000
  active = true
  language = en_US
  addr_zip = 560071

如您所见,通过使用OAuth 2.0进行身份验证,您可以从Salesforce成功访问资源所有者的帐户信息。 配置文件中提供的访问令牌过期后,客户端将在下一个请求中自动重新生成访问令牌,并使用该令牌来获取资源服务器URL上可用的受保护资源。

向Google注册

Google使用OAuth 2.0对API进行身份验证,这些API可用于访问GoogleDrive,TaskQueue和CloudSQL等服务。 您可以按照https://developers.google.com/accounts/docs/OAuth2?hl=ES上的说明,设置一个应用程序来通过Google测试您的OAuth 2.0客户端。

使用Google运行客户端

既然您已经设置了符合OAuth 2.0的服务器,就可以测试客户端并从服务器中检索受保护的信息了。

您可以在Java resources / src文件夹下找到Oauth2Client_Google.config文件。 该配置文件是针对Google服务定制的,可以用作提供配置值的模板。

如前所述 ,在Tomcat服务器上运行Oauth2Client Web应用程序。 获取访问令牌和访问受保护的资源操作的流程和输出如图6所示。

图6.访问令牌请求(登录提示)
访问令牌请求(登录提示)

图6显示了从Google接收到302和Location标头后,客户端将用户代理(网络浏览器)重定向到的Google登录页面。

图7显示了资源所有者的批准提示(登录后),以授予对Cast Iron App的访问权限,该应用程序正在请求访问资源所有者的数据。

图7.访问令牌请求(批准提示)
铸铁批准画面

资源所有者向客户端授予访问权限后,令牌端点将使用访问令牌和刷新令牌进行响应。 您应该在控制台窗口中看到类似于清单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服务器上运行它。 单击开始测试访问受保护的资源。

您应该在控制台窗口中看到类似于清单14所示的代码片段的输出。

清单14。
********** Response Received **********
  name = Varun Ojha
  features = [{"featureName":"ocr"},{"featureName":"translation","featureRate":0.5}]
  quotaBytesTotal = 16106127360
  largestChangeId = 1911
  rootFolderId = 0ALupA2Opqyp1Uk9PVA
  quotaType = LIMITED
  quotaBytesByService = [{"bytesUsed":"229","serviceName":"DRIVE"},{"bytesUsed":"1154860956","serviceName":"GMAIL"},{"bytesUsed":"568174452","serviceName":"PHOTOS"}]
  permissionId = 01822620516456565194
  quotaBytesUsedInTrash = 119332

如您所见,通过使用OAuth 2.0进行身份验证,您可以从GoogleDrive成功访问资源所有者的帐户信息。

配置文件中提供的访问令牌到期后,客户端将在下一个请求中自动重新生成访问令牌,并使用重新生成的令牌来检索资源服务器URL上可用的受保护资源。

使用IBM端点测试客户端

此客户端示例代码已通过与OAuth 2.0兼容的IBM端点(例如IBMWebsphere®Application Server和IBMDatapower.®)成功测试。

在“ 使用OAuth:在WebSphere Application Server中启用OAuth服务提供程序 ”中可以找到在Websphere Application Server上设置OAuth 2.0服务器的说明。

可以像测试Salesforce和Google一样测试IBM端点。

结论

本教程系列的第3部分介绍了授权码授予类型的基础。 它演示了如何编写Java代码中的通用OAuth 2.0客户端以连接到多个OAuth 2.0兼容的端点并从中检索受保护的资源。 可以将“ 下载 ”中的样本客户机Web项目导入到Eclipse环境中,并将其用作创建自定义Oauth 2.0客户机的起点。


翻译自: https://www.ibm.com/developerworks/security/library/se-oauthjavapt3/index.html

软件授权注册码