HttpClient Apache(四)身份验证

HTTP authentication

HttpClient完全支持HTTP标准规范定义的身份验证方案以及许多广泛使用的非标准身份验证方案,如NTLM和SPNEGO。

User credentials(用户凭据)

任何用户身份验证过程都需要一组可用于建立用户身份的凭据。
在最简单的形式中,用户凭证可以只是用户名/密码对。
UsernamePasswordCredentials表示由明文组成的安全主体和密码组成的一组凭据。
此实现足以用于HTTP标准规范定义的标准身份验证方案。

1
2
3
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());

NTCredentials是Microsoft Windows特定的实现,除了用户名/密码对之外还包括一组其他Windows特定属性,例如用户域的名称。
在Microsoft Windows网络中,同一用户可以属于多个域,每个域具有不同的授权集。

1
2
3
NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());

Authentication schemes(认证方案)

AuthScheme接口代表 面向对象的抽象 质询-响应 认证方案。认证方案有望支持以下功能:

  • 解析并处理目标服务器发送的质询,以响应对受保护资源的请求。
  • 提供已处理质询的属性:身份验证方案类型及其参数,例如此身份验证方案适用的域(如果可用)
  • 为给定的凭证集和HTTP请求生成授权字符串以响应实际的授权质询。

请注意,身份验证方案可能是有状态的,涉及一系列质询 - 响应交换。
HttpClient附带了几个AuthScheme实现:

  • Basic: RFC 2617中定义的基本身份验证方案。此身份验证方案不安全,因为凭据以明文形式传输。
    尽管不安全,但如果与TLS / SSL加密结合使用,基本身份验证方案就足够了。
  • Digest:RFC 2617中定义的摘要式身份验证方案。摘要式身份验证方案比Basic更安全,对于那些不希望通过TLS / SSL加密实现完全传输安全性开销的应用程序而言,它们是一个不错的选择。
  • NTLM:NTLM是Microsoft开发的专有身份验证方案,针对Windows平台进行了优化。
    NTLM被认为比Digest更安全。
  • SPNEGO(简单和受保护的GSSAPI协商机制)是GSSAPI“伪机制”,用于协商许多可能的实际机制之一。
    SPNEGO最明显的用途是在Microsoft的HTTP Negotiate身份验证扩展中。
    可协商的子机制包括Active Directory支持的NTLM和Kerberos。
    目前HttpClient仅支持Kerberos子机制。
  • Kerberos:Kerberos身份验证实现。

Credentials provider(凭证提供程序)

凭据提供程序旨在维护一组用户凭据,并能够为特定的身份验证范围生成用户凭据。
身份验证范围包括主机名,端口号,域名和身份验证方案名称。
在凭证提供程序中注册凭据时,可以提供通配符(任何主机,任何端口,任何领域,任何方案)而不是具体的属性值。
如果无法找到直接匹配,则凭证提供者可以找到特定范围的最接近匹配。
HttpClient可以与实现CredentialsProvider接口的凭证提供程序的任何物理表示一起使用。
名为BasicCredentialsProvider的默认CredentialsProvider实现是一个由java.util.HashMap支持的简单实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
credsProvider.setCredentials(
new AuthScope("somehost", 8080),
new UsernamePasswordCredentials("u2", "p2"));
credsProvider.setCredentials(
new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
new UsernamePasswordCredentials("u3", "p3"));

System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 80, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, null, "ntlm")));

stdout >

[principal: u1]
[principal: u2]
null
[principal: u3]

HTTP authentication and execution context(HTTP身份验证和执行上下文)

HttpClient依赖于AuthState类来跟踪有关身份验证过程状态的详细信息。
HttpClient在HTTP请求执行过程中创建两个AuthState实例:一个用于目标主机身份验证,另一个用于代理身份验证。
如果目标服务器或代理需要用户身份验证,则相应的AuthScope实例将使用身份验证过程中使用的AuthScope,AuthScheme和Crednetials进行填充。
可以检查AuthState以查明请求的身份验证类型,是否找到匹配的AuthScheme实现以及凭据提供程序是否设法查找给定身份验证范围的用户凭据。

在HTTP请求执行过程中,HttpClient将以下与身份验证相关的对象添加到执行上下文中:

  • Lookup instance representing the actual authentication scheme registry. The value of this attribute set in the local context takes precedence over the default one.

  • CredentialsProvider instance representing the actual credentials provider. The value of this attribute set in the local context takes precedence over the default one.

  • AuthState instance representing the actual target authentication state. The value of this attribute set in the local context takes precedence over the default one.

  • AuthState instance representing the actual proxy authentication state. The value of this attribute set in the local context takes precedence over the default one.

  • AuthCache instance representing the actual authentication data cache. The value of this attribute set in the local context takes precedence over the default one.

本地HttpContext对象可用于在请求执行之前自定义HTTP身份验证上下文,或在请求执行后检查其状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CloseableHttpClient httpclient = <...>

CredentialsProvider credsProvider = <...>
Lookup<AuthSchemeProvider> authRegistry = <...>
AuthCache authCache = <...>

HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthSchemeRegistry(authRegistry);
context.setAuthCache(authCache);
HttpGet httpget = new HttpGet("http://somehost/");
CloseableHttpResponse response1 = httpclient.execute(httpget, context);
<...>

AuthState proxyAuthState = context.getProxyAuthState();
System.out.println("Proxy auth state: " + proxyAuthState.getState());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = context.getTargetAuthState();
System.out.println("Target auth state: " + targetAuthState.getState());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());

Caching of authentication data(缓存身份验证数据)

从版本4.1开始,HttpClient会自动缓存有关已成功通过身份验证的主机的信息。
请注意,必须使用相同的执行上下文来执行逻辑上相关的请求,以便缓存的身份验证数据从一个请求传播到另一个请求。
一旦执行上下文超出范围,身份验证数据就会丢失。

Preemptive authentication(抢先认证)

HttpClient不支持开箱即用的抢先身份验证,因为如果误用或使用不当,抢先身份验证可能会导致严重的安全问题,例如以明文形式向未经授权的第三方发送用户凭据。
因此,期望用户在其特定应用环境的背景下评估抢先认证与安全风险的潜在好处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CloseableHttpClient httpclient = <...>

HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials("username", "password"));

// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);

// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);

HttpGet httpget = new HttpGet("/");
for (int i = 0; i < 3; i++) {
CloseableHttpResponse response = httpclient.execute(
targetHost, httpget, context);
try {
HttpEntity entity = response.getEntity();

} finally {
response.close();
}