HttpClient的认证机制,并给出示例代码。

来源:互联网 发布:办公室坐垫 知乎 编辑:程序博客网 时间:2024/06/09 19:26

HttpClient的认证机制,并给出示例代码。


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
Java代码  收藏代码
  1. import org.apache.commons.httpclient.HttpClient;  
  2. import org.apache.commons.httpclient.UsernamePasswordCredentials;  
  3. import org.apache.commons.httpclient.auth.AuthScope;  
  4. import org.apache.commons.httpclient.methods.GetMethod;  
  5.   
  6. /** 
  7.  * A simple example that uses HttpClient to perform a GET using Basic 
  8.  * Authentication. Can be run standalone without parameters. 
  9.  *  
  10.  * You need to have JSSE on your classpath for JDK prior to 1.4 
  11.  *  
  12.  * @author Michael Becke 
  13.  */  
  14. public class BasicAuthenticationExample {  
  15.   
  16.     /** 
  17.      * Constructor for BasicAuthenticatonExample. 
  18.      */  
  19.     public BasicAuthenticationExample() {  
  20.         super();  
  21.     }  
  22.   
  23.     public static void main(String[] args) throws Exception {  
  24.         HttpClient client = new HttpClient();  
  25.   
  26.         // pass our credentials to HttpClient, they will only be used for  
  27.         // authenticating to servers with realm "realm" on the host  
  28.         // "www.verisign.com", to authenticate against  
  29.         // an arbitrary realm or host change the appropriate argument to null.  
  30.         client.getState().setCredentials(  
  31.                 new AuthScope("www.verisign.com"443"realm"),  
  32.                 new UsernamePasswordCredentials("username""password"));  
  33.   
  34.         // create a GET method that reads a file over HTTPS, we're assuming  
  35.         // that this file requires basic authentication using the realm above.  
  36.         GetMethod get = new GetMethod(  
  37.                 "https://www.verisign.com/products/index.html");  
  38.   
  39.         // Tell the GET method to automatically handle authentication. The  
  40.         // method will use any appropriate credentials to handle basic  
  41.         // authentication requests. Setting this value to false will cause  
  42.         // any request for authentication to return with a status of 401.  
  43.         // It will then be up to the client to handle the authentication.  
  44.         get.setDoAuthentication(true);  
  45.   
  46.         try {  
  47.             // execute the GET  
  48.             int status = client.executeMethod(get);  
  49.   
  50.             // print the status and response  
  51.             System.out.println(status + "\n" + get.getResponseBodyAsString());  
  52.   
  53.         } finally {  
  54.             // release any connection resources used by the method  
  55.             get.releaseConnection();  
  56.         }  
  57.     }  
  58. }  
 
4.2 Alternate authentication
Java代码  收藏代码
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. import org.apache.commons.httpclient.HttpClient;  
  5. import org.apache.commons.httpclient.UsernamePasswordCredentials;  
  6. import org.apache.commons.httpclient.auth.AuthPolicy;  
  7. import org.apache.commons.httpclient.auth.AuthScope;  
  8. import org.apache.commons.httpclient.methods.GetMethod;  
  9.   
  10. /** 
  11.  * <p> 
  12.  * A simple example that uses alternate authentication scheme selection if 
  13.  * several authentication challenges are returned. 
  14.  * </p> 
  15.  *  
  16.  * <p> 
  17.  * Per default HttpClient picks the authentication challenge in the following 
  18.  * order of preference: NTLM, Digest, Basic. In certain cases it may be 
  19.  * desirable to force the use of a weaker authentication scheme. 
  20.  * </p> 
  21.  *  
  22.  * @author Oleg Kalnichevski 
  23.  */  
  24. public class AlternateAuthenticationExample {  
  25.   
  26.     /** 
  27.      * Constructor for BasicAuthenticatonExample. 
  28.      */  
  29.     public AlternateAuthenticationExample() {  
  30.         super();  
  31.     }  
  32.   
  33.     public static void main(String[] args) throws Exception {  
  34.         HttpClient client = new HttpClient();  
  35.         client.getState().setCredentials(  
  36.                 new AuthScope("myhost"80"myrealm"),  
  37.                 new UsernamePasswordCredentials("username""password"));  
  38.         // Suppose the site supports several authetication schemes: NTLM and  
  39.         // Basic  
  40.         // Basic authetication is considered inherently insecure. Hence, NTLM  
  41.         // authentication  
  42.         // is used per default  
  43.   
  44.         // This is to make HttpClient pick the Basic authentication scheme over  
  45.         // NTLM & Digest  
  46.         List<String> authPrefs = new ArrayList<String>(3);  
  47.         authPrefs.add(AuthPolicy.BASIC);  
  48.         authPrefs.add(AuthPolicy.NTLM);  
  49.         authPrefs.add(AuthPolicy.DIGEST);  
  50.         client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY,  
  51.                 authPrefs);  
  52.   
  53.         GetMethod httpget = new GetMethod(  
  54.                 "http://myhost/protected/auth-required.html");  
  55.   
  56.         try {  
  57.             int status = client.executeMethod(httpget);  
  58.             // print the status and response  
  59.             System.out.println(status);  
  60.             System.out.println(httpget.getStatusLine());  
  61.             System.out.println(httpget.getResponseBodyAsString());  
  62.         } finally {  
  63.             // release any connection resources used by the method  
  64.             httpget.releaseConnection();  
  65.         }  
  66.     }  
  67. }  
 
4.3 Custom authentication
Java代码  收藏代码
  1. import java.util.ArrayList;  
  2. import java.util.Collection;  
  3.   
  4. import org.apache.commons.httpclient.Credentials;  
  5. import org.apache.commons.httpclient.HttpMethod;  
  6. import org.apache.commons.httpclient.auth.AuthPolicy;  
  7. import org.apache.commons.httpclient.auth.AuthScheme;  
  8. import org.apache.commons.httpclient.auth.AuthenticationException;  
  9. import org.apache.commons.httpclient.auth.MalformedChallengeException;  
  10. import org.apache.commons.httpclient.params.DefaultHttpParams;  
  11. import org.apache.commons.httpclient.params.HttpParams;  
  12.   
  13. /** 
  14.  * A simple custom AuthScheme example. The included auth scheme is meant for 
  15.  * demonstration purposes only. It does not actually implement a usable 
  16.  * authentication method. 
  17.  */  
  18. public class CustomAuthenticationExample {  
  19.   
  20.     @SuppressWarnings("unchecked")  
  21.     public static void main(String[] args) {  
  22.   
  23.         // register the auth scheme  
  24.         AuthPolicy.registerAuthScheme(SecretAuthScheme.NAME,  
  25.                 SecretAuthScheme.class);  
  26.   
  27.         // include the scheme in the AuthPolicy.AUTH_SCHEME_PRIORITY preference,  
  28.         // this can be done on a per-client or per-method basis but we'll do it  
  29.         // globally for this example  
  30.         HttpParams params = DefaultHttpParams.getDefaultParams();  
  31.         ArrayList<String> schemes = new ArrayList<String>();  
  32.         schemes.add(SecretAuthScheme.NAME);  
  33.         schemes.addAll((Collection) params  
  34.                 .getParameter(AuthPolicy.AUTH_SCHEME_PRIORITY));  
  35.         params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);  
  36.   
  37.         // now that our scheme has been registered we can execute methods  
  38.         // against  
  39.         // servers that require "Secret" authentication...  
  40.     }  
  41.   
  42.     /** 
  43.      * A custom auth scheme that just uses "Open Sesame" as the authentication 
  44.      * string. 
  45.      */  
  46.     private class SecretAuthScheme implements AuthScheme {  
  47.   
  48.         public static final String NAME = "Secret";  
  49.   
  50.         public SecretAuthScheme() {  
  51.             // All auth schemes must have a no arg constructor.  
  52.         }  
  53.   
  54.         public String authenticate(Credentials credentials, HttpMethod method)  
  55.                 throws AuthenticationException {  
  56.             return "Open Sesame";  
  57.         }  
  58.   
  59.         public String authenticate(Credentials credentials, String method,  
  60.                 String uri) throws AuthenticationException {  
  61.             return "Open Sesame";  
  62.         }  
  63.   
  64.         public String getID() {  
  65.             return NAME;  
  66.         }  
  67.   
  68.         public String getParameter(String name) {  
  69.             // this scheme does not use parameters, see RFC2617Scheme for an  
  70.             // example  
  71.             return null;  
  72.         }  
  73.   
  74.         public String getRealm() {  
  75.             // this scheme does not use realms  
  76.             return null;  
  77.         }  
  78.   
  79.         public String getSchemeName() {  
  80.             return NAME;  
  81.         }  
  82.   
  83.         public boolean isConnectionBased() {  
  84.             return false;  
  85.         }  
  86.   
  87.         public void processChallenge(String challenge)  
  88.                 throws MalformedChallengeException {  
  89.             // Nothing to do here, this is not a challenge based  
  90.             // auth scheme. See NTLMScheme for a good example.  
  91.         }  
  92.   
  93.         public boolean isComplete() {  
  94.             // again we're not a challenge based scheme so this is always true  
  95.             return true;  
  96.         }  
  97.     }  
  98. }  
 
4.4 Interactive authentication
 
Java代码  收藏代码
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4.   
  5. import org.apache.commons.httpclient.Credentials;  
  6. import org.apache.commons.httpclient.HttpClient;  
  7. import org.apache.commons.httpclient.NTCredentials;  
  8. import org.apache.commons.httpclient.UsernamePasswordCredentials;  
  9. import org.apache.commons.httpclient.auth.AuthScheme;  
  10. import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;  
  11. import org.apache.commons.httpclient.auth.CredentialsProvider;  
  12. import org.apache.commons.httpclient.auth.NTLMScheme;  
  13. import org.apache.commons.httpclient.auth.RFC2617Scheme;  
  14. import org.apache.commons.httpclient.methods.GetMethod;  
  15.   
  16. /** 
  17.  * A simple example that uses HttpClient to perform interactive authentication. 
  18.  *  
  19.  * @author Oleg Kalnichevski 
  20.  */  
  21. public class InteractiveAuthenticationExample {  
  22.   
  23.     /** 
  24.      * Constructor for InteractiveAuthenticationExample. 
  25.      */  
  26.     public InteractiveAuthenticationExample() {  
  27.         super();  
  28.     }  
  29.   
  30.     public static void main(String[] args) throws Exception {  
  31.   
  32.         InteractiveAuthenticationExample demo = new InteractiveAuthenticationExample();  
  33.         demo.doDemo();  
  34.     }  
  35.   
  36.     private void doDemo() throws IOException {  
  37.   
  38.         HttpClient client = new HttpClient();  
  39.         client.getParams().setParameter(CredentialsProvider.PROVIDER,  
  40.                 new ConsoleAuthPrompter());  
  41.         GetMethod httpget = new GetMethod(  
  42.                 "http://target-host/requires-auth.html");  
  43.         httpget.setDoAuthentication(true);  
  44.         try {  
  45.             // execute the GET  
  46.             int status = client.executeMethod(httpget);  
  47.             // print the status and response  
  48.             System.out.println(status);  
  49.             System.out.println(httpget.getStatusLine().toString());  
  50.             System.out.println(httpget.getResponseBodyAsString());  
  51.         } finally {  
  52.             // release any connection resources used by the method  
  53.             httpget.releaseConnection();  
  54.         }  
  55.     }  
  56.   
  57.     public class ConsoleAuthPrompter implements CredentialsProvider {  
  58.   
  59.         private BufferedReader in = null;  
  60.   
  61.         public ConsoleAuthPrompter() {  
  62.             super();  
  63.             this.in = new BufferedReader(new InputStreamReader(System.in));  
  64.         }  
  65.   
  66.         private String readConsole() throws IOException {  
  67.             return this.in.readLine();  
  68.         }  
  69.   
  70.         public Credentials getCredentials(final AuthScheme authscheme,  
  71.                 final String host, int port, boolean proxy)  
  72.                 throws CredentialsNotAvailableException {  
  73.             if (authscheme == null) {  
  74.                 return null;  
  75.             }  
  76.             try {  
  77.                 if (authscheme instanceof NTLMScheme) {  
  78.                     System.out.println(host + ":" + port  
  79.                             + " requires Windows authentication");  
  80.                     System.out.print("Enter domain: ");  
  81.                     String domain = readConsole();  
  82.                     System.out.print("Enter username: ");  
  83.                     String user = readConsole();  
  84.                     System.out.print("Enter password: ");  
  85.                     String password = readConsole();  
  86.                     return new NTCredentials(user, password, host, domain);  
  87.                 } else if (authscheme instanceof RFC2617Scheme) {  
  88.                     System.out.println(host + ":" + port  
  89.                             + " requires authentication with the realm '"  
  90.                             + authscheme.getRealm() + "'");  
  91.                     System.out.print("Enter username: ");  
  92.                     String user = readConsole();  
  93.                     System.out.print("Enter password: ");  
  94.                     String password = readConsole();  
  95.                     return new UsernamePasswordCredentials(user, password);  
  96.                 } else {  
  97.                     throw new CredentialsNotAvailableException(  
  98.                             "Unsupported authentication scheme: "  
  99.                                     + authscheme.getSchemeName());  
  100.                 }  
  101.             } catch (IOException e) {  
  102.                 throw new CredentialsNotAvailableException(e.getMessage(), e);  
  103.             }  
  104.         }  
  105.     }  
  106. }  
 


原创粉丝点击