我正在努力让JMeter使用NTLM身份验证。一开始,我获得了一个URL和凭证。当我在Firefox和Chrome中测试凭证时,我收到了身份验证弹出窗口,并在提供凭据后进行了身份验证。所以我用以下配置创建了一个测试计划:
我不知道域名对NTLM身份验证架构的要求。因此,最终JMeter无法进行身份验证并返回HTTP 401错误。
然后我尝试了Bad boy来记录测试脚本。当我在Bad boy中输入URL时,我收到了Windows身份验证弹出窗口,并且给定的凭据在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&amp;的实现。 4;他们都没有工作。然后我下载了源代码,看看有什么我可以挖掘的。
这两个类处理JMeter的HTTP实现:
我没有发现任何错误。
我尝试通过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 Components site。
我无法找到任何未授权的原因。 JMeter或HTTPClient是否完全支持NTLM身份验证?我在阅读上述网站上的这个说明后有些疑问:
NTLM是Microsoft和Microsoft开发的专有身份验证方案 针对Windows操作系统进行了优化
直到2008年才有 官方的,公开的,完整的协议文档。 非官方的第三方协议描述存在的结果 逆向工程的努力。真的不知道是不是 基于逆向工程的协议是完整的甚至是完整的 正确的。
Microsoft发布了MS-NLMP和MS-NTHT规范 2008年2月作为其互操作性原则倡议的一部分。
从版本4.1开始的HttpClient最初支持NTLMv1,NTLMv2和 NTLM2SessionResponse认证协议,基于相反的 工程方法。从版本4.2.3开始,HttpClient现在支持一个 更正确的实现,很大程度上基于微软自己的实现 规格。这有望纠正一些问题, 特别是自从Microsoft(Windows Server 2008 R2)开始使用以来 其协议的新实现。这个新的微软 实施导致了某些情况下的身份验证失败 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
答案 0 :(得分:0)
有关详细说明和配置详细信息,请参阅Windows Authentication with Apache JMeter指南。
答案 1 :(得分:-3)
由于其专有性,没有人完全支持NTLM,除了微软。