HttpClient 三种不同的认证方案 : Basic, Digest and NTLM. 这些方案可用于服务器或代理对客户端的认证 , 简称服务器认证或代理认证。
1 .服务器认证 (Server Authentication)
HttpClient 处理服务器认证几乎是透明的,仅需要开发人员提供登录信息 (login credentials) 。登录信息保存在 HttpState 类的实例中,可以通过 setCredentials(String realm, Credentials cred) 和 getCredentials(String realm) 来获取或设置。
HttpClient 内建的自动认证,可以通过 HttpMethod 类的 setDoAuthentication(boolean doAuthentication) 方法关闭,而且这次关闭只影响 HttpMethod 当前的实例。
1.1 抢先认证 (Preemptive Authentication)
在这种模式时, HttpClient 会主动将 basic 认证应答信息传给服务器,即使在某种情况下服务器可能返回认证失败的应答,这样做主要是为了减少连接的建立。使用该机制如下所示:
client.getParams().setAuthenticationPreemptive(true);
|
抢先认证模式也提供对于特定目标或代理的缺省认证。如果没有提供缺省的认证信息,则该模式会失效。
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(new AuthScope("myhost", 80, AuthScope.ANY_REALM), defaultcreds);
|
Httpclient 实现的抢先认证遵循 rfc2617.
A client SHOULD assume that all paths at or deeper than the depth of the last symbolic element in the path field of the Request-URI also are within the protection space specified by the Basic realm value of the current challenge. A client MAY preemptively send the corresponding Authorization header with requests for resources in that space without receipt of another challenge from the server. Similarly, when a client sends a request to a proxy, it may reuse a userid and password in the Proxy-Authorization header field without receiving another challenge from the proxy server.
|
1.2 服务器认证的安全方面考虑
当需要与不被信任的站点或 web 应用通信时,应该谨慎使用缺省的认证机制。当启动 (activate) 抢先认证模式,或者认证中没有明确给出认证域,主机的 HttpClient 将使用缺省的认证机制去试图获得目标站点的授权。
如果你提供的认证信息是敏感的,你应该指定认证域。不推荐将认证域指定为 AuthScope.ANY 。(只有在 debugging 情况下,才使用)
// To be avoided unless in debug mode
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(AuthScope.ANY, defaultcreds);
|
2. 代理认证 (proxy authentication)
除了登录信息需单独存放以外,代理认证与服务器认证几乎一致。用 setProxyCredentials(String realm, Credentials cred) 和 getProxyCredentials(String realm) 设、取登录信息。
3. 认证方案 (authentication schemes)
3.1Basic
是 HTTP 中规定最早的也是最兼容的方案,遗憾的是也是最不安全的一个方案,因为它以明码传送用户名和密码。它要求一个 UsernamePasswordCredentials 实例,可以指定服务器端的访问空间或采用默认的登录信息。
3.2 Digest
是在 HTTP1.1 中增加的一个方案,虽然不如 Basic 得到的软件支持多,但还是有广泛的使用。 Digest 方案比 Basic 方案安全得多,因它根本就不通过网络传送实际的密码,传送的是利用这个密码对从服务器传来的一个随机数 (nonce) 的加密串。
它要求一个 UsernamePasswordCredentials 实例,可以指定服务器端的访问空间或采用默认的登录信息。
3.3 NTLM
这是 HttpClient 支持的最复杂的认证协议。它 Microsoft 设计的一个私有协议,没有公开的规范说明。一开始由于设计的缺陷, NTLM 的安全性比 Digest 差,后来经过一个 ServicePack 补丁后,安全性则比较 Digest 高。
NTLM 需要一个 NTCredentials 实例。 注意,由于 NTLM 不使用访问空间 (realms) 的概念, HttpClient 利用服务器的域名作访问空间的名字。还需要注意,提供给 NTCredentials 的用户名,不要用域名的前缀 - 如 : "adrian" 是正确的,而 "DOMAIN/adrian" 则是错的。
NTLM 认证的工作机制与 basic 和 digest 有很大的差别。这些差别一般由 HttpClient 处理,但理解这些差别有助避免在使用 NTLM 认证时出现错误。
[1] 从 HttpClientAPI 的角度来看, NTLM 与其它认证方式一样的工作,差别是需要提供 'NTCredentials' 实例而不是 'UsernamePasswordCredentials'( 其实,前者只是扩展了后者 )
[2] 对 NTLM 认证,访问空间是连接到的机器的域名,这对多域名主机会有一些麻烦。只有 HttpClient 连接中指定的域名才是认证用的域名。建议将 realm 设为 null 以使用默认的设置。
[3] NTLM 只是认证了一个连接而不是一请求,所以每当一个新的连接建立就要进行一次认证,且在认证的过程中保持连接是非常重要的。 因此, NTLM 不能同时用于代理认证和服务器认证,也不能用于 HTTP1.0 连接或服务器不支持持久连接 (keep-alives) 的情况。
关于 NTLM 认证机制更详细的研究,可参考 http://davenport.sourceforge.net/ntlm.html 。
3.4 选择认证
一些服务器支持多种认证方案。假设一次只能使用一种认证方案, HttpClient 必须选择使用哪种。 HttpClient 选择是基于 NTLM, Digest, Basic 顺序的。
在具体情况下,可以更改该顺序。可通过参数 'http.auth.scheme-priority' 来实现,该参数值应该被存放在一个 String 类型的 List 中。选择优先级是按插入顺序确定的。
HttpClient client = new HttpClient();
List authPrefs = new ArrayList(2);
authPrefs.add(AuthPolicy.DIGEST);
authPrefs.add(AuthPolicy.BASIC);
// This will exclude the NTLM authentication scheme
client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
|
3.5 定制认证方案
HttpClient 本身支持 basic, digest, and NTLM 这三种认证方案。同时,它也提供了加载额外定制的认证方案的功能(通过 AuthScheme 接口实现)。需要使用定制的认证方案,必须实现下面的步骤:
[1] 实现 AuthScheme 接口。
[2] 通过 AuthPolicy.registerAuthScheme() 注册定制的 AuthScheme 。
[3] 将定制的 AuthScheme 加入到 AuthPolicy.AUTH_SCHEME_PRIORITY 中。
4 .示例
4.1Basic authentication
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
/**
* A simple example that uses HttpClient to perform a GET using Basic
* Authentication. Can be run standalone without parameters.
*
* You need to have JSSE on your classpath for JDK prior to 1.4
*
* @author Michael Becke
*/
public class BasicAuthenticationExample {
/**
* Constructor for BasicAuthenticatonExample.
*/
public BasicAuthenticationExample() {
super ();
}
public static void main(String[] args) throws Exception {
HttpClient client = new HttpClient();
// pass our credentials to HttpClient, they will only be used for
// authenticating to servers with realm "realm" on the host
// "www.verisign.com", to authenticate against
// an arbitrary realm or host change the appropriate argument to null.
client.getState().setCredentials(
new AuthScope( "www.verisign.com" , 443, "realm" ),
new UsernamePasswordCredentials( "username" , "password" )
);
// create a GET method that reads a file over HTTPS, we're assuming
// that this file requires basic authentication using the realm above.
GetMethod get = new GetMethod( "https://www.verisign.com/products/index.html" );
// Tell the GET method to automatically handle authentication. The
// method will use any appropriate credentials to handle basic
// authentication requests. Setting this value to false will cause
// any request for authentication to return with a status of 401.
// It will then be up to the client to handle the authentication.
get.setDoAuthentication( true );
try {
// execute the GET
int status = client.executeMethod( get );
// print the status and response
System. out .println(status + "/n" + get.getResponseBodyAsString());
} finally {
// release any connection resources used by the method
get.releaseConnection();
}
}
}
|
4.2 Alternate authentication
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
/**
* <p> A simple example that uses alternate authentication scheme selection
* if several authentication challenges are returned.
* </p>
*
* <p> Per default HttpClient picks the authentication challenge in the following
* order of preference: NTLM, Digest, Basic. In certain cases it may be desirable
http://www.360doc.com/content/09/0917/09/61497_6076405.shtml