zoukankan      html  css  js  c++  java
  • HttpClient 4.0.x Tips

    最近用HttpClient做一个工具做代理访问服务器的WebService,由于服务器是采用NTLM验证的,再加上网上的很多资料大多是关于HttpClient 3的,折磨了好久才搞定。这里记录一下,供以后参考使用。

    关于文中内容,我不按照手册上的写法,而是整理成小Tips,希望看起来简单轻松一点。

    1, HttpClient 4.x 库可以自己处理Cookie

    这一点误导了我很久,HttpClient 4.x提供了CookieStore来帮助程序员管理本地Cookie,并且在Http请求的过程中,如果服务器的返回中带有Set-Cookie这样的字段,当前HttpClient的CookieStore中就会增加一条新的Cookie。

    (注意:误导来了)很多网上的资料认为即便当前的HttpClient的CookieStore中有服务器返回的Cookie,HttpClient在下次请求的时候也不会主动加上,需要用户自己手动添加,添加的方法有两种,一种是直接SetHeader,一种是设置CookieStore,比如:

    if (!httpclient.getCookieStore().getCookies().isEmpty()) {
                    Cookie getCookie = httpclient.getCookieStore().getCookies().get(0);
                    BasicCookieStore cookieStore = new BasicCookieStore();	
                    cookieStore.addCookie(getCookie);
                    httpclient.setCookieStore(cookieStore);
    }
    

    但事实是:

    HttpClient是否在下次请求中携带从服务器端请求来的Cookie,完全是由设置决定的。

    httpclient.getParams.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH)
    

    如果设置为Cookie策略为BEST_MATCH的话,HttpClient会在请求中携带由服务器返回的Cookie。如果按照上面的写法,手动添加了CookieStore,那么就会在下次请求中夹带着两个Cookie,Cookie和Cookie2。

    2,NTLM如何使用HttpClient模拟

    NTLM分为很多很多版本,HttpClient 3.x是支持NTLMv1,而因为legal的原因,HttpClient 4.0.x不再支持,转而使用jcifs库来支持NTLM,但是估计还是只支持v1,而没有继续扩展支持更高级的验证身份。

    如何使用NTLM验证呢?首先NTLM过程至少要3次请求才能完成一个资源的请求,我们不需要手动模拟这三个请求,只需要使用NTLM的功能就可以了。

    配置如下:

            httpclient = new DefaultHttpClient();        
            httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
            httpclient.getCredentialsProvider().setCredentials(AuthScope.ANY, 
                    new NTCredentials(asi.getUser(), asi.getPass(), asi.SERVER_NAME, asi.getDomain()));
            List<String> authpref = new ArrayList<String>();
            authpref.add(AuthPolicy.NTLM);  
            httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, authpref);
    

    其中的NTLMSchemeFactory实现如下:

    package ntml;
    import org.apache.http.auth.AuthScheme;
    import org.apache.http.auth.AuthSchemeFactory;
    import org.apache.http.impl.auth.NTLMScheme;
    import org.apache.http.params.HttpParams;
    public class NTLMSchemeFactory implements AuthSchemeFactory {
        public AuthScheme newInstance(final HttpParams params) {
            return new NTLMScheme(new JCIFSEngine());
        }
    }
    
    package ntml;
    import jcifs.ntlmssp.NtlmFlags;
    import jcifs.ntlmssp.Type1Message;	
    import jcifs.ntlmssp.Type2Message;
    import jcifs.ntlmssp.Type3Message;	
    import jcifs.util.Base64;
    import org.apache.http.impl.auth.NTLMEngine;	
    import org.apache.http.impl.auth.NTLMEngineException;
    import java.io.IOException;	
    public final class JCIFSEngine implements NTLMEngine {
        private static final int TYPE_1_FLAGS =
                NtlmFlags.NTLMSSP_NEGOTIATE_56 |	
                NtlmFlags.NTLMSSP_NEGOTIATE_128 |	
                NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
                NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
    
                NtlmFlags.NTLMSSP_REQUEST_TARGET;
    	
        public String generateType1Msg(final String domain, final String workstation)	
                throws NTLMEngineException {	
            final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
            return Base64.encode(type1Message.toByteArray());
        }	
        public String generateType3Msg(final String username, final String password,	
                final String domain, final String workstation, final String challenge)	
                throws NTLMEngineException {
            Type2Message type2Message;	
            try {	
                type2Message = new Type2Message(Base64.decode(challenge));	
            } catch (final IOException exception) {
                throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
            }
            final int type2Flags = type2Message.getFlags();
            final int type3Flags = type2Flags	
                    & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
            final Type3Message type3Message = new Type3Message(type2Message, password, domain,
                    username, workstation, type3Flags);	
            return Base64.encode(type3Message.toByteArray());	
        }
    }
    

      

    3, NTLM中的Cookie问题

    由于NTLM的几个过程中,我们无法插一脚,所以实际的NTLM认证过程中,对于哪些服务器产生产生Cookie,并要求客户端携Cookie请求以让服务器绑定Session的情况,如果不利用tips 1里面的自动携带Cookie,可能就不能做什么了。这也是前一段时间卡住最主要的内容。

    4,下一阶段准备尝试使用HttpClient编写一个库来爬Baidu或者douban的音乐...已经有Python的了,哈哈。。。

  • 相关阅读:
    wireshake抓包,飞秋发送信息,python
    python问题:IndentationError:expected an indented block错误解决《转》
    560. Subarray Sum Equals K
    311. Sparse Matrix Multiplication
    170. Two Sum III
    686. Repeated String Match
    463. Island Perimeter
    146. LRU Cache
    694. Number of Distinct Islands
    200. Number of Islands
  • 原文地址:https://www.cnblogs.com/mmjx/p/2278150.html
Copyright © 2011-2022 走看看