JMeter完全支持NTLM身份验证吗?

zpqajqem  于 2022-10-15  发布在  Java
关注(0)|答案(2)|浏览(158)

我正在努力让JMeter使用NTLM身份验证。起初,我得到了一个URL和凭证。当我在Firefox和Chrome中测试凭证时,我收到了验证弹出窗口,在提供凭证后,我得到了验证。因此,我创建了一个具有以下配置的测试计划:

  • HTTP授权管理器
  • HTTP请求默认值
  • HTTP请求

我不知道NTLM身份验证架构的域要求。因此,JMeter最终未能进行身份验证,并返回HTTP 401错误。
然后我试着让坏男孩录下测试脚本。当我在Bad boy中输入URL时,我收到了Windows身份验证弹出窗口,而给定的凭据在Bad boy中不起作用。
因此,我尝试了IE,并收到了相同的Windows身份验证弹出窗口,但凭据不起作用。我请求了该域,并在IE中将该域作为域\用户名提供后,我成功地验证了该用户。
我对JMeter也尝试了同样的方法,并在HTTP授权管理器中提供了域。不幸的是,它在JMeter中不起作用。以下是我的测试计划。我已经用别名替换了原始URL、域和凭据。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <jmeterTestPlan version="1.2" properties="2.8" jmeter="2.13 r1665067">
  3. <hashTree>
  4. <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
  5. <stringProp name="TestPlan.comments"></stringProp>
  6. <boolProp name="TestPlan.functional_mode">false</boolProp>
  7. <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
  8. <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
  9. <collectionProp name="Arguments.arguments"/>
  10. </elementProp>
  11. <stringProp name="TestPlan.user_define_classpath"></stringProp>
  12. </TestPlan>
  13. <hashTree>
  14. <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
  15. <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
  16. <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
  17. <boolProp name="LoopController.continue_forever">false</boolProp>
  18. <stringProp name="LoopController.loops">1</stringProp>
  19. </elementProp>
  20. <stringProp name="ThreadGroup.num_threads">1</stringProp>
  21. <stringProp name="ThreadGroup.ramp_time">1</stringProp>
  22. <longProp name="ThreadGroup.start_time">1429694411000</longProp>
  23. <longProp name="ThreadGroup.end_time">1429694411000</longProp>
  24. <boolProp name="ThreadGroup.scheduler">false</boolProp>
  25. <stringProp name="ThreadGroup.duration"></stringProp>
  26. <stringProp name="ThreadGroup.delay"></stringProp>
  27. </ThreadGroup>
  28. <hashTree>
  29. <AuthManager guiclass="AuthPanel" testclass="AuthManager" testname="HTTP Authorization Manager" enabled="true">
  30. <collectionProp name="AuthManager.auth_list">
  31. <elementProp name="" elementType="Authorization">
  32. <stringProp name="Authorization.url">https://my_domain</stringProp>
  33. <stringProp name="Authorization.username">username</stringProp>
  34. <stringProp name="Authorization.password">password</stringProp>
  35. <stringProp name="Authorization.domain">NTLM_DOMAIN</stringProp>
  36. <stringProp name="Authorization.realm"></stringProp>
  37. </elementProp>
  38. </collectionProp>
  39. </AuthManager>
  40. <hashTree/>
  41. <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
  42. <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
  43. <collectionProp name="Arguments.arguments"/>
  44. </elementProp>
  45. <stringProp name="HTTPSampler.domain">my_domain</stringProp>
  46. <stringProp name="HTTPSampler.port"></stringProp>
  47. <stringProp name="HTTPSampler.connect_timeout"></stringProp>
  48. <stringProp name="HTTPSampler.response_timeout"></stringProp>
  49. <stringProp name="HTTPSampler.protocol">https</stringProp>
  50. <stringProp name="HTTPSampler.contentEncoding"></stringProp>
  51. <stringProp name="HTTPSampler.path"></stringProp>
  52. <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
  53. <stringProp name="HTTPSampler.concurrentPool">4</stringProp>
  54. </ConfigTestElement>
  55. <hashTree/>
  56. <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
  57. <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
  58. <collectionProp name="Arguments.arguments"/>
  59. </elementProp>
  60. <stringProp name="HTTPSampler.domain"></stringProp>
  61. <stringProp name="HTTPSampler.port"></stringProp>
  62. <stringProp name="HTTPSampler.connect_timeout"></stringProp>
  63. <stringProp name="HTTPSampler.response_timeout"></stringProp>
  64. <stringProp name="HTTPSampler.protocol">https</stringProp>
  65. <stringProp name="HTTPSampler.contentEncoding"></stringProp>
  66. <stringProp name="HTTPSampler.path">/</stringProp>
  67. <stringProp name="HTTPSampler.method">GET</stringProp>
  68. <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
  69. <boolProp name="HTTPSampler.auto_redirects">true</boolProp>
  70. <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
  71. <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
  72. <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
  73. <boolProp name="HTTPSampler.monitor">false</boolProp>
  74. <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
  75. </HTTPSamplerProxy>
  76. <hashTree/>
  77. </hashTree>
  78. </hashTree>
  79. </hashTree>
  80. </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进行身份验证的实现:

  1. import java.io.IOException;
  2. import org.apache.commons.httpclient.Credentials;
  3. import org.apache.commons.httpclient.HttpClient;
  4. import org.apache.commons.httpclient.HttpException;
  5. import org.apache.commons.httpclient.HttpMethod;
  6. import org.apache.commons.httpclient.HttpState;
  7. import org.apache.commons.httpclient.NTCredentials;
  8. import org.apache.commons.httpclient.auth.AuthScope;
  9. import org.apache.commons.httpclient.methods.GetMethod;
  10. public class NTLMAuthenticationHttpClient {
  11. public static void main(String[] args) throws HttpException, IOException {
  12. HttpClient client = new HttpClient();
  13. Credentials credentials = new NTCredentials("username", "password", "", "NTLM_DOMAIN");
  14. HttpState state = client.getState();
  15. state.setCredentials(AuthScope.ANY, credentials);
  16. String domain = "my_domain";
  17. String protocol = "https";
  18. HttpMethod method = new GetMethod(protocol + "://" + domain);
  19. method.setDoAuthentication(true);
  20. int status = client.executeMethod(method);
  21. System.out.println(status);
  22. }
  23. }

这段代码一两次返回了HTTP 401,大多数时候我得到的是HTTP 200。
以下是使用httpclient-4.4.1的实现:

  1. import java.io.IOException;
  2. import jcifs.ntlmssp.NtlmFlags;
  3. import jcifs.ntlmssp.Type1Message;
  4. import jcifs.ntlmssp.Type2Message;
  5. import jcifs.ntlmssp.Type3Message;
  6. import jcifs.util.Base64;
  7. import org.apache.http.HttpHost;
  8. import org.apache.http.HttpResponse;
  9. import org.apache.http.auth.AuthScheme;
  10. import org.apache.http.auth.AuthSchemeProvider;
  11. import org.apache.http.auth.AuthScope;
  12. import org.apache.http.auth.NTCredentials;
  13. import org.apache.http.client.ClientProtocolException;
  14. import org.apache.http.client.CredentialsProvider;
  15. import org.apache.http.client.config.AuthSchemes;
  16. import org.apache.http.client.methods.HttpGet;
  17. import org.apache.http.config.Registry;
  18. import org.apache.http.config.RegistryBuilder;
  19. import org.apache.http.impl.auth.BasicSchemeFactory;
  20. import org.apache.http.impl.auth.DigestSchemeFactory;
  21. import org.apache.http.impl.auth.KerberosSchemeFactory;
  22. import org.apache.http.impl.auth.NTLMEngine;
  23. import org.apache.http.impl.auth.NTLMEngineException;
  24. import org.apache.http.impl.auth.NTLMScheme;
  25. import org.apache.http.impl.auth.SPNegoSchemeFactory;
  26. import org.apache.http.impl.client.CloseableHttpClient;
  27. import org.apache.http.impl.client.HttpClients;
  28. import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
  29. import org.apache.http.protocol.HttpContext;
  30. public class NTLMAuthenticationHttpComponent {
  31. public static void main(String[] args) throws ClientProtocolException,
  32. IOException {
  33. Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder
  34. .<AuthSchemeProvider> create()
  35. .register(AuthSchemes.NTLM, new AuthSchemeProvider() {
  36. public AuthScheme create(HttpContext context) {
  37. return new NTLMScheme(new JCIFSEngine());
  38. }
  39. }).register(AuthSchemes.BASIC, new BasicSchemeFactory())
  40. .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
  41. .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
  42. .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
  43. .build();
  44. String domain = "my_domain";
  45. String protocol = "https";
  46. HttpHost targetHost = new HttpHost(domain, 443, protocol);
  47. CredentialsProvider credentialsProvider = new SystemDefaultCredentialsProvider();
  48. credentialsProvider.setCredentials(
  49. new AuthScope(targetHost.getHostName(), targetHost.getPort()),
  50. new NTCredentials("username", "password", null, "NTLM_DOMAIN"));
  51. CloseableHttpClient client = HttpClients.custom()
  52. .setDefaultAuthSchemeRegistry(authSchemeRegistry)
  53. .setDefaultCredentialsProvider(credentialsProvider).build();
  54. HttpGet httpget = new HttpGet(protocol + "//" + domain);
  55. HttpResponse response = client.execute(httpget);
  56. System.out.println(response.getStatusLine().getStatusCode());
  57. }
  58. private static final class JCIFSEngine implements NTLMEngine {
  59. private static final int TYPE_1_FLAGS =
  60. NtlmFlags.NTLMSSP_NEGOTIATE_56 |
  61. NtlmFlags.NTLMSSP_NEGOTIATE_128 |
  62. NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
  63. NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
  64. NtlmFlags.NTLMSSP_REQUEST_TARGET;
  65. public String generateType1Msg(final String domain, final String workstation)
  66. throws NTLMEngineException {
  67. final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
  68. return Base64.encode(type1Message.toByteArray());
  69. }
  70. public String generateType3Msg(final String username, final String password,
  71. final String domain, final String workstation, final String challenge)
  72. throws NTLMEngineException {
  73. Type2Message type2Message;
  74. try {
  75. type2Message = new Type2Message(Base64.decode(challenge));
  76. } catch (final IOException exception) {
  77. throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
  78. }
  79. final int type2Flags = type2Message.getFlags();
  80. final int type3Flags = type2Flags
  81. & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
  82. final Type3Message type3Message = new Type3Message(type2Message, password, domain,
  83. username, workstation, type3Flags);
  84. return Base64.encode(type3Message.toByteArray());
  85. }
  86. }
  87. }

这总是返回Http 401 Unauthorized错误。此代码取自使用JCIFS的HTTP Components site
我无法找到未经授权的任何原因。JMeter或HTTPClient是否完全支持NTLM身份验证?在阅读了上述网站的注解后,我有一些疑问:
NTLM是Microsoft开发的专有身份验证方案,针对Windows操作系统进行了优化。
直到2008年,还没有正式的、公开的、完整的议定书文件。由于逆向工程的努力,存在非正式的第三方协议描述。目前尚不清楚基于逆向工程的协议是否完整甚至正确。
作为其互操作性原则倡议的一部分,Microsoft于2008年2月发布了MS-NLMP和MS-NTHT规范。
从4.1版开始,HttpClient最初支持基于反向工程方法的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中,以下是我使用Response Header得到的结果:

  1. Thread Name: Thread Group 1-1
  2. Sample Start: 2015-04-26 14:26:39 IST
  3. Load time: 3837
  4. Connect Time: 2716
  5. Latency: 3837
  6. Size in bytes: 940
  7. Headers size in bytes: 940
  8. Body size in bytes: 0
  9. Sample Count: 1
  10. Error Count: 1
  11. Response code: 401
  12. Response message: Unauthorized
  13. Response headers:
  14. HTTP/1.1 401 Unauthorized
  15. Cache-Control: max-age=0
  16. Content-Type: text/plain
  17. Date: Sun, 26 Apr 2015 08:56:42 GMT
  18. Expires: Sun, 26 Apr 2015 08:56:43 GMT
  19. Server: Apache-Coyote/1.1
  20. Set-Cookie: JSESSIONID=0D39812DAECAED077E7A9001864874A9.schbapxu1044_SEP; Expires=Sun, 26-Apr-2015 16:56:42 GMT; Path=/; Secure; HttpOnly
  21. Set-Cookie: dtCookie=2929007D72E613D13BF40F8241EC4B9F|X2RlZmF1bHR8MQ; Path=/; Domain=.my_domain_part2
  22. Set-Cookie: AWSELB=C5C5577906943F772312365AC913FBE510FFA9A080FC6FD7778CB3F66B01593D16E110291976D6D7D50FBFB1DB51745A84041319D726B0F898FAE4520DC36E25BB9AE95FBCB14D902FBC9B5903E8BCB6E32414584F;PATH=/;EXPIRES=Sun, 26-Apr-2015 16:56:42 GMT;SECURE;HTTPONLY
  23. Vary: Accept-Encoding
  24. Via: 1.1 my_domain_part1.my_domain_part2
  25. WWW-Authenticate: NTLM
  26. X-Content-Type-Options: nosniff
  27. X-dynaTrace-JS-Agent: true
  28. X-Frame-Options: SAMEORIGIN
  29. X-XSS-Protection: 1
  30. Content-Length: 0
  31. Connection: keep-alive
  32. HTTPSampleResult fields:
  33. ContentType: text/plain
  34. DataEncoding: null
jv2fixgn

jv2fixgn1#

它确实为您提供了HTTP授权管理器中的用户名、密码和域
有关详细说明和配置详细信息,请参阅Windows Authentication with Apache JMeter指南。

uubf1zoe

uubf1zoe2#

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

相关问题