簡體   English   中英

JMeter 是否完全支持 NTLM 身份驗證?

[英]Does JMeter fully support NTLM Authentication?

我正在努力讓 JMeter 使用 NTLM 身份驗證。 一開始,我得到了一個 URL 和憑據。 當我在 Firefox 和 Chrome 中測試憑據時,我收到了身份驗證彈出窗口,並在提供憑據后通過了身份驗證。 所以我創建了一個具有以下配置的測試計划:

  • HTTP 授權管理器
  • HTTP 請求默認值
  • HTTP 請求

我不知道 NTLM 身份驗證架構的域要求。 所以最終 JMeter 未能通過身份驗證並返回 HTTP 401 錯誤。

然后我嘗試了 Bad boy 來錄制測試腳本。 當我在 Bad boy 中輸入 URL 時,我收到了 Windows Authentication Popup 並且給定的憑據在 Bad boy 中不起作用。

所以我嘗試了 IE 並收到了相同的 Windows 身份驗證彈出窗口,但憑據不起作用。 我詢問了域,並在 IE 中將該域作為域\用戶名提供后,我成功地對該用戶進行了身份驗證。

我對 JMeter 進行了同樣的嘗試,並在 HTTP 授權管理器中提供了域。 不幸的是,它在 JMeter 中不起作用。 以下是我的測試計划。 我已將原始 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>

我對讓 JMeter 工作感到沮喪。 我已經嘗試過 HttpClient3.1 和 4 的實現; 他們都沒有工作。 然后我下載了源代碼,看看是否有什么可以挖掘的。

這兩個類處理 JMeter 的 HTTP 實現:

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

我沒發現有什么不對。

我嘗試通過 Java 代碼進行身份驗證。 以下是使用 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);
    }
}

這段代碼曾經返回過一次或兩次 HTTP 401,而大多數時候我得到的是 HTTP 200。

以下是使用 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());
        }
    }
}

這總是返回 Http 401 Unauthorized 錯誤。 此代碼取自使用 JCIFS 的HTTP 組件站點

我找不到任何未授權的原因。 JMeter 或 HTTPClient 是否完全支持 NTLM 身份驗證? 從上述網站閱讀此說明后,我有一些疑問:

NTLM 是 Microsoft 開發的專有身份驗證方案,並針對 Windows 操作系統進行了優化。

直到 2008 年,還沒有正式的、公開的、完整的協議文檔。 由於逆向工程的努力,存在非官方的第 3 方協議描述。 目前尚不清楚基於逆向工程的協議是否完整甚至正確。

Microsoft 於 2008 年 2 月發布了 MS-NLMP 和 MS-NTHT 規范,作為其互操作性原則計划的一部分。

HttpClient 4.1 版最初支持基於逆向工程方法的 NTLMv1、NTLMv2 和 NTLM2SessionResponse 身份驗證協議。 從版本 4.2.3 開始,HttpClient 現在支持更正確的實現,這在很大程度上基於 Microsoft 自己的規范。 這有望糾正許多問題,尤其是在 Microsoft(從 Windows Server 2008 R2 開始)開始使用其協議的新實現之后。 這種新的 Microsoft 實施在某些情況下會導致一些舊的 NTLM 逆向工程客戶端實施的身份驗證失敗。

已知新的 HttpClient NTLM 實現已成功嘗試至少針對以下系統:

  • Windows Server 2000 和 Server 2003 系統,配置為使用 LM 和 NTLMv1 身份驗證
  • Windows Server 2003 系統,配置為使用 NTLMv2 身份驗證
  • Windows Server 2008 R2 系統,配置為使用 NTLM2SessionResponse 身份驗證

如果當前的 HttpClient NTLM 實現在您的環境中被證明是有問題的,我們肯定希望聽到它。

在瀏覽器中,當我瀏覽用於身份驗證的 URL 時,它要求提供憑據,然后導航到主頁。 還有另一個中間頁面返回 HTTP 302 進行重定向。

任何建議都會對我有很大幫助。

更新:

在 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

它確實讓您在HTTP 授權管理器中提供用戶名、密碼和域

有關詳細說明和配置詳細信息,請參閱Windows Authentication with Apache JMeter指南。

由於 NTLM 的專有性質,除了 Microsoft 之外,沒有人完全支持 NTLM。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM