HTTP 是众所周知的 Internet 协议。尽管 java.net 包提供基本的通过 HTTP 协议访问资源的功能,但是它既不够灵活,功能也不是足够强大,不能满足现代 web 应用程序的需求。Apache Jakarta Commons HttpClient 使用最新的 HTTP 标准和规范,通过在客户端提供一个高效且功能丰富的包实现来填补这一空白。HttpClient 也被开源项目和商业软件产品广泛采用。
在本文中,学习如何扩展 HttpClient 认证模型。使用第三方 OAuth 库来添加 OAuth 认证。本文还讨论了 HttpClient v3.0.x 和 v4.x 之间的差异。
HttpClient 处理服务器认证几乎是透明的。只需要提供登录证书。但是,不同版本有所差异。
- HttpClient v3.x
-
证书存储在 HttpState 实例中,可以使用
setCredentials(AuthScope authscope, Credentials cred)和getCredentials(AuthScope authscope)方法设置或检索。建立在 HttpClient 上的自动授权可以禁用,使用HttpMethod类的setDoAuthentication(boolean doAuthentication)方法。更改只影响那个方法实例。也支持先占式(Preemptive)基础认证功能,通过设置
setAuthenticationPreemptive(Boolean preemptive)实现,但是只 支持先占式基础服务。 - HttpClient v4.x
- 您应该实例化
CredentialProvider来维护用户证书集,并为一个特定认证范围提供证书。通过将CredentialProvider添加到 HttpContext,这表示一个 HTTP 流程的执行状态,HttpClient 能够根据主机名、端口号和范围自动进行认证。先占式认证不再以开箱即用的方式提供,滥用先占式认证可能引起用户证书泄漏。然而,如果您想要一个先占式认证,可以使用一个标准 HttpClient 扩展(比如协议拦截器)启用。
OAuth 是一个开放的协议,以一种简单且标准的方法支持来自桌面和 web 应用程序的安全 API 授权。有了 OAuth,一个资源所有者可以授权第三方应用程序访问受保护的资源,而不损害用户证书。(OAuth 协议于 2007 年 10 月在版本 1.0 中确定,在 2009 年 6 月(修订版 A)进行改进。OAuth 2.0 规范正在开发之中。)
图 1 显示了典型的 three-legged OAuth dance。
图 1. Three-legged OAuth dance
依照上图编号,当 OAuth three-legged 握手启动时:
- 客户为 OAuth 握手请求一个临时令牌。这个令牌用于维护握手会话。
- 确认了客户之后,服务器提供商颁发一个短期请求令牌。
- 客户发送一个 HTTP 重定向响应用户浏览器,然后将用户引导到服务供应商进行授权。
- 用户检查授权请求,并在服务提供商网站上授予客户访问权限(如果他信任该客户)。
- 服务器供应商确定授权,然后发送一个 HTTP 重定向来响应用户浏览器。
- 用户浏览器被重定向到客户回调 URL,在这里客户可以完成握手的其余部分。
- 客户使用上一步传递的验证器从服务提供商请求访问令牌。
- 成功确认之后,服务提供商颁发访问令牌来访问受保护资源。
- OAuth 握手完成之后,访问令牌颁发,客户可以使用这个访问令牌代表用户访问受保护的数据。
- 服务提供商验证每个到来的 OAuth 请求,如果客户被授权,就返回受保护资源。
当保护的 OAuth 资源被请求后,通常,客户端将得到一个 HTTP 401 的响应,包含一个 WWW-Authentication 头部:
WWW-Authenticate: OAuth realm=<your_realm> |
WWW-Authentication 头部指出保护资源的认证模式。然后,HttpClient 可以根据 WWW-Authenticate 头部执行 OAuth 认证。
默认情况下,在 HttpClient 中仅支持基础认证、摘要认证和 NTLM 认证。下一小节 介绍如何在 HttpClient 模式下添加和使用 OAuth 认证。
除了原生支持基础、摘要和 NTLM 认证之外,HttpClient 有一个机制来插入自定义的额外认证模式,使用 AuthScheme 接口。要使用一个定制的认证模式:
- 实现 AuthScheme 接口。
- 使用 AuthPolicy.registerAuthScheme() 注册自定义的 AuthScheme 。
- 在 AuthPolicy.AUTH_SCHEME_PRIORITY 首选项中包含这个自定义的 AuthScheme(见本文后面的 HttpClient 3.0.x OAuth 支持)。
使用 oauth.net 的 Java 库来扩展 HttpClient 4.0.1
Oauth.net 提供一个开源 Java™ 库。如上所述,要使用自定义的认证,您需要提供您自己的 AuthScheme 和证书类。OAuth 库已经实现了它自己的 OAuthScheme、OAuthSchemeFactory 和 OAuthCredentials。您可以利用它们来添加 OAuth 支持到您的 HttpClient 应用程序。
要启用 HttpClient 4.0.1 的一个 OAuth 认证模式:
- 在 HttpClient 中注册这个新的 OAuth 认证机制。
- 提供一个新
OAuthCredentials。 - HttpClient 使用一个有序偏好来选择正确的认证机制。您可以使用本地
HttpContext对象来在请求执行之前定制 HTTP 认证内容,或者您可以在请求执行之后检查其状态。通过设置HttpContext对象的http.auth.scheme-pref属性修改认证默认首选项。
清单 1 显示了一个示例。注意代码中获取您自己的 OAuthAccessor 的方法被省略了,因为它由您的具体实现而定。
清单 1. 启用 HttpClient 4.0.1 的一个 OAuth 认证模式
AbstractHttpClient httpClient = new DefaultHttpClient();
//register the OAuthScheme
httpClient.getAuthSchemes().register(OAuthSchemeFactory.SCHEME_NAME,
new OAuthSchemeFactory());
//get the OAuthAccessor object
OAuthAccessor accessor = yourMethodToGetOAuthAccessor();
//set credentials
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM,
OAuthSchemeFactory.SCHEME_NAME),
new OAuthCredentials(accessor));
//Adjust the authentication scheme selection
HttpContext localContext = new BasicHttpContext();
localContext.setAttribute("http.auth.scheme-pref", Arrays.asList(new String[] {
"oauth", "digest", "basic"
}));
|
很多 web 网站支持 OAuth,比如 LinkedIn。以下示例展示如何使用有 OAuth 支持的 HttpClient 从 LinkedIn 获取一个用户的配置文件。
- 您需要一个 LinkedIn 账户,需要创建一个应用程序。然后您可以获取 API Key,这在 OAuth 术语中称为 Consumer Key。图 2 显示了一个例子。
图 2. 获取您应用程序的 Consumer Key 和 Secret
- 有了 Consumer Key 和 Secret Key 之后,向 LinkedIn 发出一个请求来获取 AccessToken 和 Secret,使用以下内容:
- LinkedIn Oauth 端点 URL
- 根路径:https://api.linkedin.com
- 请求令牌路径:/uas/oauth/requestToken
- 访问令牌路径:/uas/oauth/accessToken
- 授权路径:/uas/oauth/authorize
清单 2 是样例代码。
清单 2. 从 LinkedIn 获取AccessToken和SecretString baseURL = "https://api.linkedin.com"; String requestTokenURL = baseURL + "/requestToken"; String authorizationURL = baseURL + "/authorize"; String accessTokenURL = baseURL + "/accessToken"; String consumerKey="hP80ApmoJkO-9ZHuXC97olUzD1egVI75zKoff9SCKFFTY9zjc vWRRRbiNrWbcKIX"; String consumerSecret="toAk3oV1wKuon9W51lfELLHtZSxBZHih-qMyeDIBrIB2Y1hCASbpmK313 Wubmrd2"; OAuthServiceProvider provider = new OAuthServiceProvider( requestTokenURL, authorizationURL, accessTokenURL); OAuthConsumer consumer = new OAuthConsumer( "DemoOAuth", consumerKey, consumerSecret, provider); OAuthAccessor accessor = new OAuthAccessor(consumer); OAuthClient client = new OAuthClient(new HttpClient4()); List<Parameter> parameters = new ArrayList<OAuth.Parameter>(); parameters.add(new Parameter("oauth_callback", "yourAppcallbackurl")); OAuthMessage msg = client.getRequestTokenResponse(accessor, "POST", parameters); String requestToken = msg.getParameter(OAuth.OAUTH_TOKEN); String requestSecret = msg.getParameter(OAuth.OAUTH_TOKEN_SECRET);
现在您有了 URL:
authorizationURL + "?" + OAuth.OAUTH_TOKEN+ "=" + requestToken
如果在一个 web 应用程序中,用户应该访问 LinkedIn 或被重定向到 LinkedIn 以得到批准,如下所示。
图 3. 用户批准
在您自己的回调 servlet 中,使用清单 3 中的代码获取验证器,然后使用它来请求
AccessToken和Secret。
清单 3. 请求AccessToken和SecretOAuthMessage msg = OAuthServlet.getMessage(request, null); String requestToken = msg.getParameter(OAuth.OAUTH_TOKEN); String verifier = msg.getParameter(OAuth.OAUTH_VERIFIER); … get the accessor object in List 1… …. OAuthClient oauthClient = new OAuthClient(new HttpClient4()); List<Parameter> list = new ArrayList<Parameter>(); list.add(new Parameter(OAuth.OAUTH_VERIFIER, verifier)); OAuthMessage returned = oauthClient.getAccessToken(accessor, "POST", list); String accessToken = returned.getParameter(OAuth.OAUTH_TOKEN)); String accessKey = returned.getParameter(OAuth.OAUTH_TOKEN_SECRET));
- 添加 OAuth 支持到 HttpClient。
使用上述代码,注册
OAuthScheme。使用accessor对像设置证书。 - 使用 OAuth 认证发送请求到 LinkedIn。
清单 4. 使用 OAuth 认证发送请求到 LinkedInHttpGet httpget = new HttpGet("https://api.linkedin.com/v1/people/~"); //Run the http get method under the modified context httpClient.execute(httpget, localContext);
现在您可以使用 OAuth 认证通过 HttpClient 获取用户配置文件,如清单 5 所示。
清单 5. LinkedIn 返回用户信息<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <person> <first-name>Zheng</first-name> <last-name>BI</last-name> <headline>SE at IBM</headline> <site-standard-profile-request> <url>http://www.linkedin.com/profile?viewProfile=&key=84546207 &authToken=SMl9&authType=name& trk=api*a113393*s121886*</url> </site-standard-profile-request> </person>
HttpClient 不支持开箱即用的先占式认证,但是您可以使用一个协议拦截器事先引入一个 AuthScheme 实例到执行上下文。这个拦截器必须在标准认证拦截器之前 添加到协议处理链。
仅支持 OAuth 1.0 的 web 网站不提供 “挑战” 响应。要使用 OAuth 认证,您需要使用先占式认证。OAuth 库也使用 HttpClient 4.0.1 的 HttpRequestInterceptor 使其得以实现。清单 6 中显示的样例代码可以启用先占式认证。
清单 6. 启用先占式认证
HttpRequestInterceptor preemptiveAuth = new PreemptiveAuthorizer();
httpClient.addRequestInterceptor(preemptiveAuth, 0);
|
HttpClient 原生支持基础认证、摘要认证和 NTLM 认证。在 HttpClient 3.0.x 中添加一个自定义的 AuthScheme 与在 4.0.1 中添加有所不同。
- OAuth Java Lib 不提供 HttpClient 3.0.x 支持,因此您需要创建您自己的
Scheme类,来实现 AuthScheme 接口。关于编写该函数的解释不在本文讨论范围中。 - 使用
AuthPolicy.registerAuthScheme()注册 OAuthScheme(见 http://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/auth/AuthPolicy.html#registerAuthScheme%28java.lang.String,%20java.lang.Class%29)。 - 更改
AuthPolicy.AUTH_SCHEME_PRIORITY首选项来启用自定义的AuthScheme,如下所示。
清单 7. 启用自定义的AuthSchemeHttpClient client = new HttpClient(); List authPrefs = new ArrayList(2); authPrefs.add(AuthPolicy.OAUTH); authPrefs.add(AuthPolicy.DIGEST); authPrefs.add(AuthPolicy.BASIC); client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
HttpClient 3.0.x 也支持先占式认证,如下所示。
清单 8. HttpClient 3.0.x 支持先占式认证
client.getParams().setAuthenticationPreemptive(true);
|
然而,在该模式下,您只能使用基础认证。对于一个需要 OAuth 先占式认证的网站,您不能使用 HttpClient 3.0.x。
HttpClient 认证模式提供一种机制来进行自身扩展,对于在开发期间使用 HttpClient 的应用程序来说,利用一个第三方 OAuth 库来添加 OAuth 认证是比较容易的。然而对于 HttpClient 3.0.x 有一些限制。
学习
- 阅读 OAuth 1.0 Protocol,这是 Internet Engineering Task Force (IETF) 的一个产品,代表 IETF 社区的共识。
- 从 Apache 阅读更多关于 HttpCore 和 HttpClient 4.1 的信息。
- 阅读 认证机制,使用 HTTP 服务器或者端口进行认证。
- 访问 OAuth.net 获取 OAuth 1.0 和 2.0 各个方面的信息,包括与 OAuth 协议实现相关的代码,比如本文中所用的 Java OAuth 库。
- 研究 LinkedIn
DeveloperNetwork,寻找 LinkedIn 开源 API。
-
developerWorks Web development
专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
-
developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
-
developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。
- 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。
获得产品和技术
-
IBM 产品评估试用版软件 或 IBM SOA 人员沙箱,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- 分享您的知识:加入一个关注 web 主题的 developerWorks 小组。
- Roland Barcia 在其博客中讨论了 Web 2.0 和中间件。
- 关注 developerWorks 成员 关于 web 主题的共享书签。
- 快速找到答案:访问 Web 2.0 Apps 论坛。
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

