我正在调用一个用.net编写的远程运行的.net服务。
我使用apache axis 1.4
和eclipse IDE创建了各自的存根,并创建了一个相应的Web服务客户端。这实际上只是一个测试客户端,我的Web应用程序将调用此Web服务。
我们保留了两个不同的端点,用于保持安全凭证启用/禁用。
(401)未经授权 (401)未经授权的AxisFault faultCode:{http://xml.apache.org/axis/} HTTP faultSubcode: faultString:(401)未经授权 faultActor: faultNode: faultDetail:{}:返回代码:401
我想传递这种格式的凭证: 1)域\用户名 2)密码
我尝试在这里添加其他帖子的建议,其中说明在stub中设置相应的before call方法但是我得到了相同的上述异常。
(mystub)._ setProperty(javax.xml.rpc.Stub.USERNAME_PROPERTY,domain +“\”+ username); (mystub)._ setProperty(javax.xml.rpc.Stub.PASSWORD_PROPERTY,password);
但是现在通过一些搜索我能够通过java独立程序调用我的远程.net Web服务来执行NTLM身份验证:
public static void main(String[] args) throws Exception {
String urlStr = “http://example.com/root/action.dll?p1=value1″;
String domain = “”; // May also be referred as realm
String userName = “CHANGE_ME”;
String password = “CHANGE_ME”;
String responseText = getAuthenticatedResponse(urlStr, domain, userName, password);
System.out.println(”response: ” + responseText);
}
private static String getAuthenticatedResponse(final String urlStr, final String domain, final String userName, final String password) throws IOException {
StringBuilder response = new StringBuilder();
Authenticator.setDefault(new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(domain + “\\” + userName, password.toCharArray());
}
});
URL urlRequest = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) urlRequest.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod(”GET”);
InputStream stream = conn.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String str = “”;
while ((str = in.readLine()) != null) {
response.append(str);
}
in.close();
return response.toString();
}
但我无法使用我的轴客户端,因为存根是由我的Web服务客户端中的.net Web服务提供的wsdl生成的。我尝试通过修改根据上面的演示在invoke()调用之前更改@stub级别,但它会抛出相同的未经授权的异常。
这只是fyi /所有远程IIS服务器使用NTLM身份验证技术。
使用java进行Windows身份验证时需要帮助,以便将安全凭据传递给IIS。
[注意:我的轴客户端(java)使用密码传递域\用户,这在其他方面正确配置了IIS服务器]
问题是Axis 1.4没有正确实现NTLM V2协议。
我遇到了Sharepoint 2010 Web服务的问题。我有一个客户端与在Windows 2003服务器上运行的Sharepoint 2007完美配合。然后,我使用在Windows 2008 R2服务器上运行的Sharepoint 2010 Web服务测试了此客户端,并且它们停止工作。错误是:
Caused by: (401)Unauthorized
at org.apache.axis.transport.http.CommonsHTTPSender.invoke(CommonsHTTPSender.java:218)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:165)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at org.apache.axis.client.Call.invoke(Call.java:1812)
在谷歌搜索,问题是Windows 2003默认使用NTLM V1协议,而Windows 2008 R2默认使用NTLM V2。
我发现解决方案和问题在以下网址中完美解释:
http://devsac.blogspot.com.es/2010/10/supoprt-for-ntlmv2-with-apache.html
解决方案是创建以下类来解析HttpClient 3.x:
public class JCIFS_NTLMScheme implements AuthScheme {
private static AppLogger logger = new AppLogger(HTTPHelper.class.getName());
/** NTLM challenge string. */
private String ntlmchallenge = null;
private static final int UNINITIATED = 0;
private static final int INITIATED = 1;
private static final int TYPE1_MSG_GENERATED = 2;
private static final int TYPE2_MSG_RECEIVED = 3;
private static final int TYPE3_MSG_GENERATED = 4;
private static final int FAILED = Integer.MAX_VALUE;
/** Authentication process state */
private int state;
public JCIFS_NTLMScheme() throws AuthenticationException {
// Check if JCIFS is present. If not present, do not proceed.
try {
Class.forName("jcifs.ntlmssp.NtlmMessage",false,this.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
throw new AuthenticationException("Unable to proceed as JCIFS library is not found.");
}
}
public String authenticate(Credentials credentials, HttpMethod method)
throws AuthenticationException {
logger.doLog(AppLogger.FINEST,
"Enter JCIFS_NTLMScheme.authenticate(Credentials, HttpMethod)",
null);
if (this.state == UNINITIATED) {
throw new IllegalStateException(
"NTLM authentication process has not been initiated");
}
NTCredentials ntcredentials = null;
try {
ntcredentials = (NTCredentials) credentials;
} catch (ClassCastException e) {
throw new InvalidCredentialsException(
"Credentials cannot be used for NTLM authentication: "
+ credentials.getClass().getName());
}
NTLM ntlm = new NTLM();
ntlm.setCredentialCharset(method.getParams().getCredentialCharset());
String response = null;
if (this.state == INITIATED || this.state == FAILED) {
response = ntlm.generateType1Msg(ntcredentials.getHost(),
ntcredentials.getDomain());
this.state = TYPE1_MSG_GENERATED;
} else {
response = ntlm.generateType3Msg(ntcredentials.getUserName(),
ntcredentials.getPassword(), ntcredentials.getHost(),
ntcredentials.getDomain(), this.ntlmchallenge);
this.state = TYPE3_MSG_GENERATED;
}
return "NTLM " + response;
}
public String authenticate(Credentials credentials, String method,
String uri) throws AuthenticationException {
throw new RuntimeException(
"Not implemented as it is deprecated anyway in Httpclient 3.x");
}
public String getID() {
throw new RuntimeException(
"Not implemented as it is deprecated anyway in Httpclient 3.x");
}
/**
* Returns the authentication parameter with the given name, if available.
*
* <p>
* There are no valid parameters for NTLM authentication so this method
* always returns <tt>null</tt>.
* </p>
*
* @param name
* The name of the parameter to be returned
*
* @return the parameter with the given name
*/
public String getParameter(String name) {
if (name == null) {
throw new IllegalArgumentException("Parameter name may not be null");
}
return null;
}
/**
* The concept of an authentication realm is not supported by the NTLM
* authentication scheme. Always returns <code>null</code>.
*
* @return <code>null</code>
*/
public String getRealm() {
return null;
}
/**
* Returns textual designation of the NTLM authentication scheme.
*
* @return <code>ntlm</code>
*/
public String getSchemeName() {
return "ntlm";
}
/**
* Tests if the NTLM authentication process has been completed.
*
* @return <tt>true</tt> if Basic authorization has been processed,
* <tt>false</tt> otherwise.
*
* @since 3.0
*/
public boolean isComplete() {
return this.state == TYPE3_MSG_GENERATED || this.state == FAILED;
}
/**
* Returns <tt>true</tt>. NTLM authentication scheme is connection based.
*
* @return <tt>true</tt>.
*
* @since 3.0
*/
public boolean isConnectionBased() {
return true;
}
/**
* Processes the NTLM challenge.
*
* @param challenge
* the challenge string
*
* @throws MalformedChallengeException
* is thrown if the authentication challenge is malformed
*
* @since 3.0
*/
public void processChallenge(final String challenge)
throws MalformedChallengeException {
String s = AuthChallengeParser.extractScheme(challenge);
if (!s.equalsIgnoreCase(getSchemeName())) {
throw new MalformedChallengeException("Invalid NTLM challenge: "
+ challenge);
}
int i = challenge.indexOf(' ');
if (i != -1) {
s = challenge.substring(i, challenge.length());
this.ntlmchallenge = s.trim();
this.state = TYPE2_MSG_RECEIVED;
} else {
this.ntlmchallenge = "";
if (this.state == UNINITIATED) {
this.state = INITIATED;
} else {
this.state = FAILED;
}
}
}
private class NTLM {
/** Character encoding */
public static final String DEFAULT_CHARSET = "ASCII";
/**
* The character was used by 3.x's NTLM to encode the username and
* password. Apparently, this is not needed in when passing username,
* password from NTCredentials to the JCIFS library
*/
private String credentialCharset = DEFAULT_CHARSET;
void setCredentialCharset(String credentialCharset) {
this.credentialCharset = credentialCharset;
}
private String generateType1Msg(String host, String domain) {
jcifs.ntlmssp.Type1Message t1m = new jcifs.ntlmssp.Type1Message(jcifs.ntlmssp.Type1Message.getDefaultFlags(),
domain, host);
return jcifs.util.Base64.encode(t1m.toByteArray());
}
private String generateType3Msg(String username, String password, String host,
String domain, String challenge) {
jcifs.ntlmssp.Type2Message t2m;
try {
t2m = new jcifs.ntlmssp.Type2Message(jcifs.util.Base64.decode(challenge));
} catch (IOException e) {
throw new RuntimeException("Invalid Type2 message", e);
}
jcifs.ntlmssp.Type3Message t3m = new jcifs.ntlmssp.Type3Message(t2m, password, domain,
username, host, 0);
return jcifs.util.Base64.encode(t3m.toByteArray());
}
}
}
然后使用以下命令注册新的JCIFS_NTLMScheme类作为NTLMScheme的替代:
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, org.xyz.JCIFS_NTLMScheme.class);