HttpClient 4.1.1 使用 NTLM 进行身份验证时返回 401,浏览器工作正常 [英] HttpClient 4.1.1 returns 401 when authenticating with NTLM, browsers work fine

查看:25
本文介绍了HttpClient 4.1.1 使用 NTLM 进行身份验证时返回 401,浏览器工作正常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Apache/Jakarta HttpClient 4.1.1 使用给定的凭据连接到任意网页.为了测试这一点,我在我的开发机器上安装了最少的 IIS 7.5,一次只有一种身份验证模式处于活动状态.基本身份验证工作正常,但每当我尝试登录时,摘要和 NTLM 都会返回 401 错误消息.这是我的代码:

I'm trying to use the Apache/Jakarta HttpClient 4.1.1 to connect to an arbitrary web page using the given credentials. To test this, I have a minimal install of IIS 7.5 on my dev machine running where only one authentication mode is active at a time. Basic authentication works fine, but Digest and NTLM return 401 error messages whenever I try to log in. Here is my code:

    DefaultHttpClient httpclient = new DefaultHttpClient();
    HttpContext localContext = new BasicHttpContext();
    HttpGet httpget = new HttpGet("http://localhost/"); 
    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(AuthScope.ANY,
            new NTCredentials("user", "password", "", "localhost"));
    if (!new File(System.getenv("windir") + "\krb5.ini").exists()) {
        List<String> authtypes = new ArrayList<String>();
        authtypes.add(AuthPolicy.NTLM);
        authtypes.add(AuthPolicy.DIGEST);
        authtypes.add(AuthPolicy.BASIC);
        httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF,
                authtypes);
        httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,
                authtypes);
    }
    localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
    HttpResponse response = httpclient.execute(httpget, localContext);
    System.out.println("Response code: " + response.getStatusLine());

我在 Fiddler 中注意到的一件事是 Firefox 发送的哈希值与 HttpClient 发送的哈希值不同,这让我认为 IIS 7.5 可能期望比 HttpClient 提供的哈希值更强?有任何想法吗?如果我能验证这是否适用于 NTLM,那就太好了.摘要也不错,但如果有必要,我可以没有它.

The one thing I've noticed in Fiddler is that the hashes sent by Firefox versus by HttpClient are different, making me think that maybe IIS 7.5 is expecting stronger hashing than HttpClient provides? Any ideas? It'd be great if I could verify that this would work with NTLM. Digest would be nice too, but I can live without that if necessary.

推荐答案

我不是这方面的专家,但在使用 http 组件的 NTLM 身份验证期间,我发现客户端需要 3 次尝试才能连接到 NTML 端点就我而言.对于 Spnego,此处 有点描述但是NTLM认证有点不同.

I am not an expert on the subject but during the NTLM authentication using http components I have seen that the client needs 3 attempts in order to connect to an NTML endpoint in my case. It is kinda described here for Spnego but it is a bit different for the NTLM authentication.

对于第一次尝试的 NTLM,客户端将使用 Target auth state: UNCHALLENGED 发出请求,Web 服务器返回 HTTP 401 状态和标头:WWW-Authenticate: NTLM

For NTLM in the first attempt client will make a request with Target auth state: UNCHALLENGED and Web server returns HTTP 401 status and a header: WWW-Authenticate: NTLM

客户端将检查已配置的身份验证方案,应在客户端代码中配置 NTLM.

Client will check for the configured Authentication schemes, NTLM should be configured in client code.

第二次尝试,客户端将使用 Target auth state: CHALLENGED 发出请求,并将发送带有以 base64 格式编码的令牌的授权标头:Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw==服务器再次返回 HTTP 401 状态,但标头:WWW-Authenticate: NTLM 现在填充了编码信息.

Second attempt, client will make a request with Target auth state: CHALLENGED, and will send an authorization header with a token encoded in base64 format: Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw== Server again returns HTTP 401 status but the header: WWW-Authenticate: NTLM now is populated with encoded information.

第三次尝试客户端将使用来自 WWW-Authenticate: NTLM 标头的信息,并将使用 Target auth state: HANDSHAKE 和授权标头 发出最终请求授权:NTLM,其中包含服务器的更多信息.

3rd Attempt Client will use the information from WWW-Authenticate: NTLM header and will make the final request with Target auth state: HANDSHAKE and an authorisation header Authorization: NTLM which contains more information for the server.

在我的情况下,我收到一个 HTTP/1.1 200 OK 之后.

In my case I receive an HTTP/1.1 200 OK after that.

为了在每个请求中避免这一切documentation 在第 4.7.1 章指出,相同的执行令牌必须用于逻辑相关的请求.对我来说它没有用.

In order to avoid all this in every request documentation at chapter 4.7.1 states that the same execution token must be used for logically related requests. For me it did not worked.

我的代码:我在 EJB 的 @PostConstruct 方法中初始化客户端一次

My code: I initialize the client once in a @PostConstruct method of an EJB

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(18);
        cm.setDefaultMaxPerRoute(6);

        RequestConfig requestConfig = RequestConfig.custom()
        .setSocketTimeout(30000)
        .setConnectTimeout(30000)
        .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
        .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
        .build();

        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                new NTCredentials(userName, password, hostName, domainName));

        // Finally we instantiate the client. Client is a thread safe object and can be used by several threads at the same time. 
        // Client can be used for several request. The life span of the client must be equal to the life span of this EJB.
         this.httpclient = HttpClients.custom()
        .setConnectionManager(cm)
        .setDefaultCredentialsProvider(credentialsProvider)
        .setDefaultRequestConfig(requestConfig)
        .build();

在每个请求中使用相同的客户端实例:

Use the same client instance in every request:

            HttpPost httppost = new HttpPost(endPoint.trim());            
            // HttpClientContext is not thread safe, one per request must be created.
            HttpClientContext context = HttpClientContext.create();    
            response = this.httpclient.execute(httppost, context);

在我的 EJB 的 @PreDestroy 方法中解除分配资源并将连接返回给连接管理器:

Deallocate the resources and return the connection back to connection manager, at the @PreDestroy method of my EJB:

             this.httpclient.close();

这篇关于HttpClient 4.1.1 使用 NTLM 进行身份验证时返回 401,浏览器工作正常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆