简体   繁体   English

JMeter 是否完全支持 NTLM 身份验证?

[英]Does JMeter fully support NTLM Authentication?

I am struggling to make JMeter to work with NTLM authentication.我正在努力让 JMeter 使用 NTLM 身份验证。 At the beginning I was provided with an URL and credential.一开始,我得到了一个 URL 和凭据。 When I tested the credential in Firefox and in Chrome I received the authentication popup and upon providing the credential I was authenticated.当我在 Firefox 和 Chrome 中测试凭据时,我收到了身份验证弹出窗口,并在提供凭据后通过了身份验证。 So I had created a Test Plan with the following configuration:所以我创建了一个具有以下配置的测试计划:

  • HTTP Authorization Manager HTTP 授权管理器
  • HTTP Request Default HTTP 请求默认值
  • HTTP Request HTTP 请求

I didn't know about the requirement of the Domain for the NTLM Authentication Schema.我不知道 NTLM 身份验证架构的域要求。 So eventually the JMeter was failing to authenticate and was returning HTTP 401 error.所以最终 JMeter 未能通过身份验证并返回 HTTP 401 错误。

Then I tried Bad boy to record the test script.然后我尝试了 Bad boy 来录制测试脚本。 When I had entered the URL in Bad boy I received Windows Authentication Popup and the given credential didn't work in Bad boy.当我在 Bad boy 中输入 URL 时,我收到了 Windows Authentication Popup 并且给定的凭据在 Bad boy 中不起作用。

So I tried the IE and received same Windows Authentication Popup and the credential didn't worked.所以我尝试了 IE 并收到了相同的 Windows 身份验证弹出窗口,但凭据不起作用。 I asked for the domain and upon providing that domain in IE as Domain\Username I was successful to authenticate that user.我询问了域,并在 IE 中将该域作为域\用户名提供后,我成功地对该用户进行了身份验证。

I tried the same with the JMeter and have provided the Domain in the HTTP Authorization Manager.我对 JMeter 进行了同样的尝试,并在 HTTP 授权管理器中提供了域。 Unfortunately it didn't work in JMeter.不幸的是,它在 JMeter 中不起作用。 Following is my Test Plan.以下是我的测试计划。 I have replaced the original URL, domain and the credential with aliases.我已将原始 URL、域和凭证替换为别名。

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.8" jmeter="2.13 r1665067">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <longProp name="ThreadGroup.start_time">1429694411000</longProp>
        <longProp name="ThreadGroup.end_time">1429694411000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <AuthManager guiclass="AuthPanel" testclass="AuthManager" testname="HTTP Authorization Manager" enabled="true">
          <collectionProp name="AuthManager.auth_list">
            <elementProp name="" elementType="Authorization">
              <stringProp name="Authorization.url">https://my_domain</stringProp>
              <stringProp name="Authorization.username">username</stringProp>
              <stringProp name="Authorization.password">password</stringProp>
              <stringProp name="Authorization.domain">NTLM_DOMAIN</stringProp>
              <stringProp name="Authorization.realm"></stringProp>
            </elementProp>
          </collectionProp>
        </AuthManager>
        <hashTree/>
        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">my_domain</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path"></stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.concurrentPool">4</stringProp>
        </ConfigTestElement>
        <hashTree/>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain"></stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">/</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">true</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <boolProp name="HTTPSampler.monitor">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
        </HTTPSamplerProxy>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

I was getting frustrated to make JMeter to work.我对让 JMeter 工作感到沮丧。 I have tried both the implementation of HttpClient3.1 & 4;我已经尝试过 HttpClient3.1 和 4 的实现; none of them have worked.他们都没有工作。 Then I have downloaded the Source Code to see if there is anything I can dig up with.然后我下载了源代码,看看是否有什么可以挖掘的。

These two classes deals with the HTTP implementation of JMeter:这两个类处理 JMeter 的 HTTP 实现:

  • org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl (for HttpClient4) org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl(用于 HttpClient4)
  • org.apache.jmeter.protocol.http.sampler.HTTPHC3Impl (for HttpClient3.1) org.apache.jmeter.protocol.http.sampler.HTTPHC3Impl(用于 HttpClient3.1)

I didn't find anything wrong.我没发现有什么不对。

I tried the authentication via Java code.我尝试通过 Java 代码进行身份验证。 Following is the implementation of the authentication using common-httpclient-3.1:以下是使用 common-httpclient-3.1 进行身份验证的实现:

import java.io.IOException;

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;

public class NTLMAuthenticationHttpClient {

    public static void main(String[] args) throws HttpException, IOException {
        HttpClient client = new HttpClient();
        Credentials credentials = new NTCredentials("username", "password", "", "NTLM_DOMAIN");
        HttpState state = client.getState();
        state.setCredentials(AuthScope.ANY, credentials);

        String domain = "my_domain";
        String protocol = "https";

        HttpMethod method = new GetMethod(protocol + "://" + domain);
        method.setDoAuthentication(true);
        int status = client.executeMethod(method);
        System.out.println(status);
    }
}

This piece of code once or twice had returned HTTP 401 and most of the time I got HTTP 200.这段代码曾经返回过一次或两次 HTTP 401,而大多数时候我得到的是 HTTP 200。

Following is the implementation using httpclient-4.4.1:以下是使用 httpclient-4.4.1 的实现:

import java.io.IOException;

import jcifs.ntlmssp.NtlmFlags;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.util.Base64;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.KerberosSchemeFactory;
import org.apache.http.impl.auth.NTLMEngine;
import org.apache.http.impl.auth.NTLMEngineException;
import org.apache.http.impl.auth.NTLMScheme;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
import org.apache.http.protocol.HttpContext;

public class NTLMAuthenticationHttpComponent {

    public static void main(String[] args) throws ClientProtocolException,
            IOException {
        Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder
                .<AuthSchemeProvider> create()
                .register(AuthSchemes.NTLM, new AuthSchemeProvider() {

                    public AuthScheme create(HttpContext context) {
                        return new NTLMScheme(new JCIFSEngine());
                    }
                }).register(AuthSchemes.BASIC, new BasicSchemeFactory())
                .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
                .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
                .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
                .build();

        String domain = "my_domain";
        String protocol = "https";

        HttpHost targetHost = new HttpHost(domain, 443, protocol);

        CredentialsProvider credentialsProvider = new SystemDefaultCredentialsProvider();
        credentialsProvider.setCredentials(
                new AuthScope(targetHost.getHostName(), targetHost.getPort()),
                new NTCredentials("username", "password", null, "NTLM_DOMAIN"));

        CloseableHttpClient client = HttpClients.custom()
                .setDefaultAuthSchemeRegistry(authSchemeRegistry)
                .setDefaultCredentialsProvider(credentialsProvider).build();

        HttpGet httpget = new HttpGet(protocol + "//" + domain);

        HttpResponse response = client.execute(httpget);
        System.out.println(response.getStatusLine().getStatusCode());
    }

    private static 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());
        }
    }
}

This is always returning Http 401 Unauthorized error.这总是返回 Http 401 Unauthorized 错误。 This code was taked from the HTTP Components site , which is using JCIFS.此代码取自使用 JCIFS 的HTTP 组件站点

I am unable to find any cause for the Unauthorization.我找不到任何未授权的原因。 Does JMeter or the HTTPClient supports NTLM Authentication fully? JMeter 或 HTTPClient 是否完全支持 NTLM 身份验证? I am having some doubt after reading this note from the above site:从上述网站阅读此说明后,我有一些疑问:

NTLM is a proprietary authentication scheme developed by Microsoft and optimized for Windows operating system. NTLM 是 Microsoft 开发的专有身份验证方案,并针对 Windows 操作系统进行了优化。

Until year 2008 there was no official, publicly available, complete documentation of the protocol.直到 2008 年,还没有正式的、公开的、完整的协议文档。 Unofficial 3rd party protocol descriptions existed as a result of reverse-engineering efforts.由于逆向工程的努力,存在非官方的第 3 方协议描述。 It was not really known whether the protocol based on the reverse-engineering were complete or even correct.目前尚不清楚基于逆向工程的协议是否完整甚至正确。

Microsoft published MS-NLMP and MS-NTHT specifications in February 2008 as a part of its Interoperability Principles initiative. Microsoft 于 2008 年 2 月发布了 MS-NLMP 和 MS-NTHT 规范,作为其互操作性原则计划的一部分。

HttpClient as of version 4.1 initially supported NTLMv1, NTLMv2, and NTLM2SessionResponse authentication protocols, based on the reverse engineering approach. HttpClient 4.1 版最初支持基于逆向工程方法的 NTLMv1、NTLMv2 和 NTLM2SessionResponse 身份验证协议。 As of version 4.2.3, HttpClient now supports a more correct implementation, based in large part on Microsoft's own specifications.从版本 4.2.3 开始,HttpClient 现在支持更正确的实现,这在很大程度上基于 Microsoft 自己的规范。 This is expected to correct a number of problems, especially since Microsoft (as of Windows Server 2008 R2) began using a new implementation of its protocols.这有望纠正许多问题,尤其是在 Microsoft(从 Windows Server 2008 R2 开始)开始使用其协议的新实现之后。 This new Microsoft implementation has led to authentication failures in some cases from some of the older reverse-engineered client implementations of NTLM.这种新的 Microsoft 实施在某些情况下会导致一些旧的 NTLM 逆向工程客户端实施的身份验证失败。

The new HttpClient NTLM implementation is known to have been tried successfully against at least the following systems:已知新的 HttpClient NTLM 实现已成功尝试至少针对以下系统:

  • Windows Server 2000 and Server 2003 systems, configured to use LM and NTLMv1 authentication Windows Server 2000 和 Server 2003 系统,配置为使用 LM 和 NTLMv1 身份验证
  • Windows Server 2003 systems, configured to use NTLMv2 authentication Windows Server 2003 系统,配置为使用 NTLMv2 身份验证
  • Windows Server 2008 R2 systems, configured to use NTLM2SessionResponse authentication Windows Server 2008 R2 系统,配置为使用 NTLM2SessionResponse 身份验证

If the current HttpClient NTLM implementation should prove problematic in your environment, we'd definitely like to hear about it.如果当前的 HttpClient NTLM 实现在您的环境中被证明是有问题的,我们肯定希望听到它。

In browser when I am browing the URL which I have used for the authentication it is asking for the credential then it is navigating to the home page.在浏览器中,当我浏览用于身份验证的 URL 时,它要求提供凭据,然后导航到主页。 There is another intermediate page which returns HTTP 302 for the redirection.还有另一个中间页面返回 HTTP 302 进行重定向。

Any suggestion will be a big help for me.任何建议都会对我有很大帮助。

Update:更新:

In JMeter following is the result I am getting with the Response Header:在 JMeter 中,以下是我使用响应头得到的结果:

Thread Name: Thread Group 1-1
Sample Start: 2015-04-26 14:26:39 IST
Load time: 3837
Connect Time: 2716
Latency: 3837
Size in bytes: 940
Headers size in bytes: 940
Body size in bytes: 0
Sample Count: 1
Error Count: 1
Response code: 401
Response message: Unauthorized

Response headers:
HTTP/1.1 401 Unauthorized
Cache-Control: max-age=0
Content-Type: text/plain
Date: Sun, 26 Apr 2015 08:56:42 GMT
Expires: Sun, 26 Apr 2015 08:56:43 GMT
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=0D39812DAECAED077E7A9001864874A9.schbapxu1044_SEP; Expires=Sun, 26-Apr-2015 16:56:42 GMT; Path=/; Secure; HttpOnly
Set-Cookie: dtCookie=2929007D72E613D13BF40F8241EC4B9F|X2RlZmF1bHR8MQ; Path=/; Domain=.my_domain_part2
Set-Cookie: AWSELB=C5C5577906943F772312365AC913FBE510FFA9A080FC6FD7778CB3F66B01593D16E110291976D6D7D50FBFB1DB51745A84041319D726B0F898FAE4520DC36E25BB9AE95FBCB14D902FBC9B5903E8BCB6E32414584F;PATH=/;EXPIRES=Sun, 26-Apr-2015 16:56:42 GMT;SECURE;HTTPONLY
Vary: Accept-Encoding
Via: 1.1 my_domain_part1.my_domain_part2
WWW-Authenticate: NTLM
X-Content-Type-Options: nosniff
X-dynaTrace-JS-Agent: true
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1
Content-Length: 0
Connection: keep-alive


HTTPSampleResult fields:
ContentType: text/plain
DataEncoding: null

It does given you provide username, password and domain in HTTP Authorization Manager它确实让您在HTTP 授权管理器中提供用户名、密码和域

SeeWindows Authentication with Apache JMeter guide for detailed explanation and configuration details.有关详细说明和配置详细信息,请参阅Windows Authentication with Apache JMeter指南。

由于 NTLM 的专有性质,除了 Microsoft 之外,没有人完全支持 NTLM。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM